2025-07-30 08:11:34 +00:00
|
|
|
|
import time
|
|
|
|
|
|
from datetime import datetime, timedelta
|
|
|
|
|
|
from typing import Optional
|
|
|
|
|
|
import pandas as pd
|
|
|
|
|
|
import okx.MarketData as Market
|
2025-07-31 12:31:22 +00:00
|
|
|
|
from core.utils import timestamp_to_datetime
|
|
|
|
|
|
from core.db.db_trade_data import DBTradeData
|
2025-08-15 03:37:06 +00:00
|
|
|
|
import core.logger as logging
|
2025-07-30 08:11:34 +00:00
|
|
|
|
|
2025-08-15 03:37:06 +00:00
|
|
|
|
logger = logging.logger
|
2025-07-30 08:11:34 +00:00
|
|
|
|
|
|
|
|
|
|
class TradeData:
|
|
|
|
|
|
def __init__(self,
|
|
|
|
|
|
api_key: str,
|
|
|
|
|
|
secret_key: str,
|
|
|
|
|
|
passphrase: str,
|
|
|
|
|
|
sandbox: bool = True,
|
|
|
|
|
|
db_url: str = None):
|
|
|
|
|
|
flag = "1" if sandbox else "0" # 0:实盘环境 1:沙盒环境
|
|
|
|
|
|
self.market_api = Market.MarketAPI(
|
|
|
|
|
|
api_key=api_key, api_secret_key=secret_key, passphrase=passphrase,
|
|
|
|
|
|
flag=flag
|
|
|
|
|
|
)
|
|
|
|
|
|
self.db_url = db_url
|
|
|
|
|
|
self.db_trade_data = DBTradeData(self.db_url)
|
|
|
|
|
|
|
|
|
|
|
|
def get_history_trades(self, symbol: str, start_ts: int, end_ts: int, limit: int = 100):
|
|
|
|
|
|
"""
|
|
|
|
|
|
获取历史交易数据
|
|
|
|
|
|
:param symbol: 交易对
|
|
|
|
|
|
:param start_ts: 起始时间(毫秒级timestamp)
|
|
|
|
|
|
:param end_ts: 结束时间(毫秒级timestamp)
|
|
|
|
|
|
:param limit: 每次请求数量
|
|
|
|
|
|
:return: pd.DataFrame
|
|
|
|
|
|
symbol-USDT)。
|
|
|
|
|
|
tradeId:交易ID。
|
|
|
|
|
|
px:交易价格(如USDT)。
|
|
|
|
|
|
sz:交易数量(如BTC数量)。
|
|
|
|
|
|
side:交易方向(buy表示买入,sell表示卖出)
|
|
|
|
|
|
ts:交易时间戳(成交时间,Unix时间戳的毫秒数格式, 如1597026383085)。
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
all_trades = []
|
|
|
|
|
|
after = end_ts # 从较晚时间开始(OKX的after表示更早的数据)
|
|
|
|
|
|
|
|
|
|
|
|
while True:
|
|
|
|
|
|
count = 0
|
|
|
|
|
|
while True:
|
|
|
|
|
|
try:
|
|
|
|
|
|
result = self.market_api.get_history_trades(
|
|
|
|
|
|
instId=symbol, # 交易对,如BTC-USDT
|
|
|
|
|
|
type=2,
|
|
|
|
|
|
limit=str(limit), # 每次最大返回100条
|
|
|
|
|
|
after=str(after) # 获取更早的数据
|
|
|
|
|
|
)
|
|
|
|
|
|
if result:
|
|
|
|
|
|
break
|
|
|
|
|
|
except Exception as e:
|
2025-08-15 03:37:06 +00:00
|
|
|
|
logger.error(f"请求出错: {e}")
|
2025-07-30 08:11:34 +00:00
|
|
|
|
count += 1
|
|
|
|
|
|
if count > 3:
|
|
|
|
|
|
break
|
|
|
|
|
|
time.sleep(3)
|
|
|
|
|
|
if result["code"] != "0":
|
|
|
|
|
|
print(f"Error: {result['msg']}")
|
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
|
|
trades = result["data"]
|
|
|
|
|
|
if not trades:
|
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
|
|
from_time = int(trades[-1]["ts"])
|
|
|
|
|
|
to_time = int(trades[0]["ts"])
|
|
|
|
|
|
from_date_time = timestamp_to_datetime(from_time)
|
|
|
|
|
|
to_date_time = timestamp_to_datetime(to_time)
|
|
|
|
|
|
|
2025-08-15 03:37:06 +00:00
|
|
|
|
logger.info(f"获得交易数据,最早时间: {from_date_time}, 最近时间: {to_date_time}")
|
2025-07-30 08:11:34 +00:00
|
|
|
|
|
|
|
|
|
|
df = pd.DataFrame(trades)
|
|
|
|
|
|
# 过滤时间范围
|
|
|
|
|
|
df["ts"] = df["ts"].astype(int)
|
|
|
|
|
|
df = df[(df["ts"] >= start_ts) & (df["ts"] <= end_ts)]
|
|
|
|
|
|
# set sz, px 为float
|
|
|
|
|
|
df["sz"] = df["sz"].astype(float)
|
|
|
|
|
|
df["px"] = df["px"].astype(float)
|
|
|
|
|
|
# 将instId重命名为symbol
|
|
|
|
|
|
df.rename(columns={"instId": "symbol"}, inplace=True)
|
|
|
|
|
|
df["date_time"] = df["ts"].apply(lambda x: timestamp_to_datetime(x))
|
|
|
|
|
|
df["tradeId"] = df["tradeId"].astype(str)
|
|
|
|
|
|
df["create_time"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
|
|
|
|
df = df[["symbol", "ts", "date_time", "tradeId", "side", "sz", "px", "create_time"]]
|
|
|
|
|
|
|
|
|
|
|
|
self.db_trade_data.insert_data_to_mysql(df)
|
|
|
|
|
|
all_trades.append(df)
|
|
|
|
|
|
# 检查最近时间戳是否超出范围
|
|
|
|
|
|
|
|
|
|
|
|
if from_time <= start_ts:
|
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
|
|
after = from_time - 1 # 更新after,继续获取更早的数据
|
|
|
|
|
|
time.sleep(0.5)
|
|
|
|
|
|
|
|
|
|
|
|
if len(all_trades) > 0:
|
|
|
|
|
|
# 转换为DataFrame
|
|
|
|
|
|
final_df = pd.concat(all_trades)
|
|
|
|
|
|
if final_df.empty:
|
|
|
|
|
|
print("No trades found in the specified time range.")
|
|
|
|
|
|
return None
|
|
|
|
|
|
else:
|
|
|
|
|
|
return final_df
|
|
|
|
|
|
else:
|
|
|
|
|
|
return None
|
|
|
|
|
|
except Exception as e:
|
2025-08-15 03:37:06 +00:00
|
|
|
|
logger.error(f"获取历史交易数据失败: {e}")
|
2025-07-30 08:11:34 +00:00
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|