crypto_quant/core/db_trade_data.py

384 lines
14 KiB
Python
Raw Normal View History

import pandas as pd
import logging
from typing import Optional, List, Dict, Any, Union
from core.db_manager import DBData
from core.utils import check_date_time_format, datetime_to_timestamp
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s: %(message)s")
class DBTradeData:
def __init__(
self,
db_url: str
):
self.db_url = db_url
self.table_name = "crypto_trade_data"
self.columns = [
"symbol",
"ts",
"date_time",
"tradeId",
"side",
"sz",
"px",
"create_time",
]
self.db_manager = DBData(db_url, self.table_name, self.columns)
def _process_time_parameter(self, time_param: Optional[Union[str, int]]) -> Optional[int]:
"""
处理时间参数统一转换为时间戳
:param time_param: 时间参数字符串或整数
:return: 时间戳或None
"""
if time_param is None:
return None
if isinstance(time_param, int):
return time_param
if isinstance(time_param, str):
if time_param.isdigit():
return int(time_param)
else:
parsed_time = check_date_time_format(time_param)
if parsed_time is None:
logging.warning(f"日期时间格式错误: {time_param}")
return None
return datetime_to_timestamp(parsed_time)
return None
def _build_query_conditions(
self,
symbol: Optional[str] = None,
side: Optional[str] = None,
start: Optional[Union[str, int]] = None,
end: Optional[Union[str, int]] = None,
additional_conditions: Optional[List[str]] = None
) -> tuple[List[str], Dict[str, Any]]:
"""
构建查询条件
:param symbol: 交易对
:param side: 交易方向
:param start: 开始时间
:param end: 结束时间
:param additional_conditions: 额外的查询条件
:return: (条件列表, 参数字典)
"""
conditions = additional_conditions or []
condition_dict = {}
if symbol:
conditions.append("symbol = :symbol")
condition_dict["symbol"] = symbol
if side:
conditions.append("side = :side")
condition_dict["side"] = side
# 处理时间参数
start_timestamp = self._process_time_parameter(start)
end_timestamp = self._process_time_parameter(end)
if start_timestamp is not None:
conditions.append("ts >= :start")
condition_dict["start"] = start_timestamp
if end_timestamp is not None:
conditions.append("ts <= :end")
condition_dict["end"] = end_timestamp
return conditions, condition_dict
def insert_data_to_mysql(self, df: pd.DataFrame) -> None:
"""
将交易数据保存到MySQL的crypto_trade_data表
速度 最快
内存 中等
适用场景中小数据量<10万条
:param df: 交易数据DataFrame
"""
if df is None or df.empty:
logging.warning("DataFrame为空无需写入数据库。")
return
self.db_manager.insert_data_to_mysql(df)
def insert_data_to_mysql_fast(self, df: pd.DataFrame) -> None:
"""
快速插入交易数据方案2使用executemany批量插入
速度 很快
内存
适用场景中等数据量
:param df: 交易数据DataFrame
"""
if df is None or df.empty:
logging.warning("DataFrame为空无需写入数据库。")
return
self.db_manager.insert_data_to_mysql_fast(df)
def insert_data_to_mysql_chunk(self, df: pd.DataFrame, chunk_size: int = 1000) -> None:
"""
分块插入交易数据方案3适合大数据量
速度 中等
内存 最低
适用场景大数据量>10万条
:param df: 交易数据DataFrame
:param chunk_size: 分块大小
"""
if df is None or df.empty:
logging.warning("DataFrame为空无需写入数据库。")
return
self.db_manager.insert_data_to_mysql_chunk(df, chunk_size)
def insert_data_to_mysql_simple(self, df: pd.DataFrame) -> None:
"""
简单插入交易数据方案4直接使用to_sql忽略重复
速度 最快
内存 中等
注意会抛出重复键错误需要额外处理
"""
if df is None or df.empty:
logging.warning("DataFrame为空无需写入数据库。")
return
self.db_manager.insert_data_to_mysql_simple(df)
def query_latest_data(self, symbol: str) -> Optional[Dict[str, Any]]:
"""
查询最新交易数据
:param symbol: 交易对
:return: 最新交易记录或None
"""
sql = """
SELECT * FROM crypto_trade_data
WHERE symbol = :symbol
ORDER BY ts DESC
LIMIT 1
"""
condition_dict = {"symbol": symbol}
return self.db_manager.query_data(sql, condition_dict, return_multi=False)
def query_earliest_data(self, symbol: str) -> Optional[Dict[str, Any]]:
"""
查询最早交易数据
:param symbol: 交易对
:return: 最早交易记录或None
"""
sql = """
SELECT * FROM crypto_trade_data
WHERE symbol = :symbol
ORDER BY ts ASC
LIMIT 1
"""
condition_dict = {"symbol": symbol}
return self.db_manager.query_data(sql, condition_dict, return_multi=False)
def query_data_by_tradeId(self, tradeId: str) -> Optional[Dict[str, Any]]:
"""
根据交易ID查询交易数据
:param tradeId: 交易ID
:return: 交易记录或None
"""
sql = """
SELECT * FROM crypto_trade_data
WHERE tradeId = :tradeId
"""
condition_dict = {"tradeId": tradeId}
return self.db_manager.query_data(sql, condition_dict, return_multi=False)
def query_trade_data_by_symbol(self, symbol: str, start: Optional[Union[str, int]] = None, end: Optional[Union[str, int]] = None) -> Optional[List[Dict[str, Any]]]:
"""
根据交易对查询交易数据
:param symbol: 交易对
:param start: 开始时间
:param end: 结束时间
:return: 交易记录列表或None
"""
conditions, condition_dict = self._build_query_conditions(symbol=symbol, start=start, end=end)
where_clause = " AND ".join(conditions) if conditions else "1=1"
sql = f"""
SELECT * FROM crypto_trade_data
WHERE {where_clause}
ORDER BY ts ASC
"""
return self.db_manager.query_data(sql, condition_dict, return_multi=True)
def query_trade_data_by_side(self, side: str, symbol: Optional[str] = None, start: Optional[Union[str, int]] = None, end: Optional[Union[str, int]] = None) -> Optional[List[Dict[str, Any]]]:
"""
根据交易方向查询交易数据
:param side: 交易方向 (buy/sell)
:param symbol: 交易对可选
:param start: 开始时间
:param end: 结束时间
:return: 交易记录列表或None
"""
conditions, condition_dict = self._build_query_conditions(symbol=symbol, side=side, start=start, end=end)
where_clause = " AND ".join(conditions)
sql = f"""
SELECT * FROM crypto_trade_data
WHERE {where_clause}
ORDER BY ts ASC
"""
return self.db_manager.query_data(sql, condition_dict, return_multi=True)
def query_buy_trades(self, symbol: Optional[str] = None, start: Optional[Union[str, int]] = None, end: Optional[Union[str, int]] = None) -> Optional[List[Dict[str, Any]]]:
"""
查询买入交易记录
:param symbol: 交易对可选
:param start: 开始时间
:param end: 结束时间
:return: 买入交易记录列表或None
"""
return self.query_trade_data_by_side("buy", symbol, start, end)
def query_sell_trades(self, symbol: Optional[str] = None, start: Optional[Union[str, int]] = None, end: Optional[Union[str, int]] = None) -> Optional[List[Dict[str, Any]]]:
"""
查询卖出交易记录
:param symbol: 交易对可选
:param start: 开始时间
:param end: 结束时间
:return: 卖出交易记录列表或None
"""
return self.query_trade_data_by_side("sell", symbol, start, end)
def get_trade_statistics(self, symbol: Optional[str] = None, start: Optional[Union[str, int]] = None, end: Optional[Union[str, int]] = None) -> Optional[Dict[str, Any]]:
"""
获取交易统计信息
:param symbol: 交易对可选
:param start: 开始时间
:param end: 结束时间
:return: 统计信息或None
"""
conditions, condition_dict = self._build_query_conditions(symbol=symbol, start=start, end=end)
where_clause = " AND ".join(conditions) if conditions else "1=1"
sql = f"""
SELECT
COUNT(*) as total_trades,
SUM(CASE WHEN side = 'buy' THEN 1 ELSE 0 END) as buy_count,
SUM(CASE WHEN side = 'sell' THEN 1 ELSE 0 END) as sell_count,
SUM(CASE WHEN side = 'buy' THEN sz ELSE 0 END) as total_buy_volume,
SUM(CASE WHEN side = 'sell' THEN sz ELSE 0 END) as total_sell_volume,
SUM(CASE WHEN side = 'buy' THEN sz * px ELSE 0 END) as total_buy_value,
SUM(CASE WHEN side = 'sell' THEN sz * px ELSE 0 END) as total_sell_value,
AVG(px) as avg_price,
MIN(px) as min_price,
MAX(px) as max_price,
MIN(ts) as first_trade_time,
MAX(ts) as last_trade_time
FROM crypto_trade_data
WHERE {where_clause}
"""
return self.db_manager.query_data(sql, condition_dict, return_multi=False)
def get_volume_price_analysis(self, symbol: Optional[str] = None, start: Optional[Union[str, int]] = None, end: Optional[Union[str, int]] = None) -> Optional[List[Dict[str, Any]]]:
"""
获取成交量价格分析
:param symbol: 交易对可选
:param start: 开始时间
:param end: 结束时间
:return: 分析结果列表或None
"""
conditions, condition_dict = self._build_query_conditions(symbol=symbol, start=start, end=end)
where_clause = " AND ".join(conditions) if conditions else "1=1"
sql = f"""
SELECT
side,
COUNT(*) as trade_count,
SUM(sz) as total_volume,
SUM(sz * px) as total_value,
AVG(px) as avg_price,
MIN(px) as min_price,
MAX(px) as max_price,
AVG(sz) as avg_volume
FROM crypto_trade_data
WHERE {where_clause}
GROUP BY side
ORDER BY side
"""
return self.db_manager.query_data(sql, condition_dict, return_multi=True)
def get_recent_trades(self, symbol: Optional[str] = None, limit: int = 100) -> Optional[List[Dict[str, Any]]]:
"""
获取最近的交易记录
:param symbol: 交易对可选
:param limit: 返回记录数量
:return: 最近交易记录列表或None
"""
conditions = []
condition_dict = {}
if symbol:
conditions.append("symbol = :symbol")
condition_dict["symbol"] = symbol
where_clause = " AND ".join(conditions) if conditions else "1=1"
sql = f"""
SELECT * FROM crypto_trade_data
WHERE {where_clause}
ORDER BY ts DESC
LIMIT :limit
"""
condition_dict["limit"] = limit
return self.db_manager.query_data(sql, condition_dict, return_multi=True)
def get_trades_by_price_range(self, min_price: float, max_price: float, symbol: Optional[str] = None, start: Optional[Union[str, int]] = None, end: Optional[Union[str, int]] = None) -> Optional[List[Dict[str, Any]]]:
"""
根据价格范围查询交易记录
:param min_price: 最低价格
:param max_price: 最高价格
:param symbol: 交易对可选
:param start: 开始时间
:param end: 结束时间
:return: 交易记录列表或None
"""
conditions, condition_dict = self._build_query_conditions(symbol=symbol, start=start, end=end)
conditions.append("px BETWEEN :min_price AND :max_price")
condition_dict["min_price"] = min_price
condition_dict["max_price"] = max_price
where_clause = " AND ".join(conditions)
sql = f"""
SELECT * FROM crypto_trade_data
WHERE {where_clause}
ORDER BY ts ASC
"""
return self.db_manager.query_data(sql, condition_dict, return_multi=True)
def get_trades_by_volume_range(self, min_volume: float, max_volume: float, symbol: Optional[str] = None, start: Optional[Union[str, int]] = None, end: Optional[Union[str, int]] = None) -> Optional[List[Dict[str, Any]]]:
"""
根据成交量范围查询交易记录
:param min_volume: 最低成交量
:param max_volume: 最高成交量
:param symbol: 交易对可选
:param start: 开始时间
:param end: 结束时间
:return: 交易记录列表或None
"""
conditions, condition_dict = self._build_query_conditions(symbol=symbol, start=start, end=end)
conditions.append("sz BETWEEN :min_volume AND :max_volume")
condition_dict["min_volume"] = min_volume
condition_dict["max_volume"] = max_volume
where_clause = " AND ".join(conditions)
sql = f"""
SELECT * FROM crypto_trade_data
WHERE {where_clause}
ORDER BY ts ASC
"""
return self.db_manager.query_data(sql, condition_dict, return_multi=True)