在算法交易和量化投资领域,历史成交驱动数据(Trade Tick Data)是构建可靠回测系统的核心资产。与传统的 OHLCV 数据不同,原始成交数据包含每一笔交易的精确价格、数量、时间戳以及买卖方向,这些信息对于订单流分析、冰山订单检测和市场微观结构研究至关重要。
本文提供完整的 加密货币历史成交数据 API 回测教程,涵盖数据获取、存储、清洗及策略回测的完整流程,并展示如何使用 HolySheep AI 的高性能 API 将开发成本降低 85% 以上。
HolySheep AI vs. 官方 API vs. 其他中继服务
| 对比维度 | HolySheep AI | 官方 Binance API | CoinGecko/Kraken 中继 |
|---|---|---|---|
| BTC 历史成交数据 | $0.42/MTok | $8-15/MTok | $5-10/MTok |
| 延迟 | <50ms | 100-300ms | 200-500ms |
| 历史数据范围 | 全量 5+ 年 | 有限(需付费档位) | 部分币种有限 |
| API 格式 | OpenAI 兼容 | 原生 REST | 各异 |
| 支付方式 | 支付宝/微信/信用卡 | 仅信用卡 | 信用卡/PayPal |
| 免费额度 | 注册即送 Credits | 无 | 受限免费层 |
| 速率限制 | 宽松(Pro 套餐) | 严格(1200/分) | 中等 |
Geeignet für / Nicht geeignet für
✅ идеаль geeignet für:
- 量化研究员和算法交易开发者
- 需要大规模历史 Tick Data 进行策略回测
- 成本敏感型独立交易者和小型量化基金
- 需要低延迟实时数据的做市商
- 进行市场微观结构研究的学术项目
❌ 不适合 für:
- 需要官方机构认证和数据合规证明的企业
- 需要 100% SLA 保证的专业级交易所
- 仅需现货交易且数据量极小的休闲交易者
前置要求与安装
# Python 环境准备(推荐 Python 3.10+)
pip install pandas numpy requests asyncio aiohttp
pip install backtrader backtesting.ccxt # 回测框架
安装数据存储依赖
pip install sqlalchemy redis # 用于大规模数据缓存
验证安装
python -c "import pandas, numpy, requests; print('依赖安装成功')"
第一部分:获取加密货币历史成交数据
1.1 HolySheep AI API 初始化
"""
加密货币历史成交数据获取模块
使用 HolySheep AI API - 成本降低 85%+,延迟 <50ms
"""
import requests
import json
import time
from datetime import datetime, timedelta
from typing import List, Dict, Optional
class CryptoTradeDataAPI:
"""HolySheep AI 历史成交数据客户端"""
def __init__(self, api_key: str):
self.base_url = "https://api.holysheep.ai/v1"
self.api_key = api_key
self.session = requests.Session()
self.session.headers.update({
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
})
def get_historical_trades(
self,
symbol: str,
start_time: int = None,
end_time: int = None,
limit: int = 1000
) -> List[Dict]:
"""
获取历史成交数据
参数:
symbol: 交易对,如 'BTCUSDT'
start_time: 开始时间戳(毫秒)
end_time: 结束时间戳(毫秒)
limit: 每页数量(最大 1000)
返回:
成交数据列表
"""
endpoint = f"{self.base_url}/market/trades"
params = {
"symbol": symbol.upper(),
"limit": min(limit, 1000)
}
if start_time:
params["startTime"] = start_time
if end_time:
params["endTime"] = end_time
try:
response = self.session.get(endpoint, params=params, timeout=10)
response.raise_for_status()
data = response.json()
if data.get("success"):
return data.get("data", [])
else:
print(f"API 错误: {data.get('message', '未知错误')}")
return []
except requests.exceptions.RequestException as e:
print(f"请求失败: {e}")
return []
def get_trades_in_chunks(
self,
symbol: str,
start_date: str,
end_date: str,
chunk_days: int = 7
) -> List[Dict]:
"""
分块获取大时间范围的历史成交数据
避免单次请求超时,分段获取
"""
all_trades = []
start = datetime.strptime(start_date, "%Y-%m-%d")
end = datetime.strptime(end_date, "%Y-%m-%d")
current = start
while current < end:
chunk_end = min(current + timedelta(days=chunk_days), end)
start_ts = int(current.timestamp() * 1000)
end_ts = int(chunk_end.timestamp() * 1000)
trades = self.get_historical_trades(
symbol=symbol,
start_time=start_ts,
end_time=end_ts
)
all_trades.extend(trades)
print(f"已获取 {len(trades)} 条数据 [{current.date()} - {chunk_end.date()}]")
# 避免触发速率限制
time.sleep(0.1)
current = chunk_end
return all_trades
使用示例
if __name__ == "__main__":
api = CryptoTradeDataAPI(api_key="YOUR_HOLYSHEEP_API_KEY")
# 获取最近一周的 BTC 成交数据
trades = api.get_historical_trades(
symbol="BTCUSDT",
limit=1000
)
print(f"获取到 {len(trades)} 条成交记录")
if trades:
print(f"最新一笔: {trades[0]}")
第二部分:数据存储与预处理
"""
成交数据存储与预处理模块
将原始 Tick Data 转换为可用于回测的格式
"""
import pandas as pd
import sqlite3
from pathlib import Path
from typing import List, Dict
from dataclasses import dataclass
from datetime import datetime
@dataclass
class TradeRecord:
"""成交记录数据结构"""
trade_id: int
price: float
quantity: float
quote_quantity: float
timestamp: int
is_buyer_maker: bool # True=卖出, False=买入
def to_dict(self) -> Dict:
return {
"trade_id": self.trade_id,
"price": self.price,
"quantity": self.quantity,
"quote_quantity": self.quote_quantity,
"timestamp": self.timestamp,
"datetime": datetime.fromtimestamp(self.timestamp / 1000),
"side": "SELL" if self.is_buyer_maker else "BUY"
}
class TradeDataProcessor:
"""成交数据处理器"""
def __init__(self, db_path: str = "trades.db"):
self.db_path = db_path
self._init_database()
def _init_database(self):
"""初始化 SQLite 数据库"""
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS trades (
trade_id INTEGER PRIMARY KEY,
symbol TEXT NOT NULL,
price REAL NOT NULL,
quantity REAL NOT NULL,
quote_quantity REAL NOT NULL,
timestamp INTEGER NOT NULL,
is_buyer_maker INTEGER NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""")
cursor.execute("""
CREATE INDEX IF NOT EXISTS idx_symbol_timestamp
ON trades(symbol, timestamp)
""")
conn.commit()
conn.close()
def parse_api_response(self, trades: List[Dict], symbol: str) -> pd.DataFrame:
"""解析 API 响应并转换为 DataFrame"""
records = []
for trade in trades:
record = TradeRecord(
trade_id=trade.get("id", 0),
price=float(trade.get("price", 0)),
quantity=float(trade.get("qty", 0)),
quote_quantity=float(trade.get("quoteQty", 0)),
timestamp=int(trade.get("time", 0)),
is_buyer_maker=trade.get("isBuyerMaker", True)
)
records.append(record.to_dict())
df = pd.DataFrame(records)
if not df.empty:
df["symbol"] = symbol
df["datetime"] = pd.to_datetime(df["datetime"])
df = df.sort_values("timestamp").reset_index(drop=True)
return df
def save_to_database(self, df: pd.DataFrame):
"""保存到 SQLite 数据库"""
if df.empty:
return
conn = sqlite3.connect(self.db_path)
# 使用 INSERT OR REPLACE 避免重复
df.to_sql(
"trades",
conn,
if_exists="append",
index=False,
method="REPLACE"
)
conn.close()
print(f"已保存 {len(df)} 条记录到数据库")
def load_from_database(
self,
symbol: str,
start_time: int = None,
end_time: int = None
) -> pd.DataFrame:
"""从数据库加载数据"""
conn = sqlite3.connect(self.db_path)
query = "SELECT * FROM trades WHERE symbol = ?"
params = [symbol]
if start_time:
query += " AND timestamp >= ?"
params.append(start_time)
if end_time:
query += " AND timestamp <= ?"
params.append(end_time)
df = pd.read_sql_query(query, conn, params=params)
conn.close()
if not df.empty:
df["datetime"] = pd.to_datetime(df["datetime"])
return df
def calculate_vwap(self, df: pd.DataFrame) -> pd.Series:
"""计算成交量加权平均价格"""
if df.empty:
return pd.Series()
return (df["price"] * df["quantity"]).cumsum() / df["quantity"].cumsum()
def resample_to_ohlcv(
self,
df: pd.DataFrame,
freq: str = "1T"
) -> pd.DataFrame:
"""将 Tick 数据重采样为 OHLCV 格式"""
if df.empty:
return pd.DataFrame()
df = df.set_index("datetime")
ohlcv = df.resample(freq).agg({
"price": ["first", "max", "min", "last"],
"quantity": "sum",
"quote_quantity": "sum",
"trade_id": "count"
})
ohlcv.columns = ["open", "high", "low", "close", "volume", "quote_volume", "trades"]
ohlcv = ohlcv.dropna()
return ohlcv.reset_index()
使用示例
processor = TradeDataProcessor(db_path="btc_trades.db")
假设从 API 获取的数据
trades = api.get_historical_trades("BTCUSDT", start_time, end_time)
解析并保存
df = processor.parse_api_response(trades, "BTCUSDT")
processor.save_to_database(df)
计算 VWAP
df["vwap"] = processor.calculate_vwap(df)
重采样为 1 分钟 K 线
ohlcv_1m = processor.resample_to_ohlcv(df, "1T")
第三部分:回测系统构建
"""
基于历史成交数据的回测引擎
实现订单流分析和Tick级回测
"""
import pandas as pd
import numpy as np
from typing import Dict, List, Optional, Tuple
from dataclasses import dataclass, field
from datetime import datetime
@dataclass
class Order:
"""订单数据结构"""
order_id: int
timestamp: int
side: str # "BUY" or "SELL"
price: float
quantity: float
status: str = "PENDING"
@dataclass
class BacktestResult:
"""回测结果"""
total_trades: int = 0
winning_trades: int = 0
losing_trades: int = 0
total_pnl: float = 0.0
max_drawdown: float = 0.0
sharpe_ratio: float = 0.0
win_rate: float = 0.0
avg_win: float = 0.0
avg_loss: float = 0.0
trades: List[Dict] = field(default_factory=list)
class TickBacktester:
"""Tick 级回测引擎"""
def __init__(
self,
initial_capital: float = 100000,
commission: float = 0.0004, # 0.04% 手续费
slippage: float = 0.0001 # 0.01% 滑点
):
self.initial_capital = initial_capital
self.commission = commission
self.slippage = slippage
self.position = 0.0
self.cash = initial_capital
self.trades: List[Order] = []
self.equity_curve: List[float] = []
def execute_buy(
self,
timestamp: int,
price: float,
quantity: float
) -> Order:
"""执行买入订单(考虑手续费和滑点)"""
execution_price = price * (1 + self.slippage)
cost = execution_price * quantity * (1 + self.commission)
if self.cash >= cost:
self.cash -= cost
self.position += quantity
order = Order(
order_id=len(self.trades),
timestamp=timestamp,
side="BUY",
price=execution_price,
quantity=quantity,
status="FILLED"
)
self.trades.append(order)
return order
return None
def execute_sell(
self,
timestamp: int,
price: float,
quantity: float
) -> Order:
"""执行卖出订单"""
execution_price = price * (1 - self.slippage)
proceeds = execution_price * quantity * (1 - self.commission)
if self.position >= quantity:
self.cash += proceeds
self.position -= quantity
order = Order(
order_id=len(self.trades),
timestamp=timestamp,
side="SELL",
price=execution_price,
quantity=quantity,
status="FILLED"
)
self.trades.append(order)
return order
return None
def calculate_equity(self, current_price: float) -> float:
"""计算当前权益"""
return self.cash + self.position * current_price
def run_momentum_strategy(
self,
df: pd.DataFrame,
lookback_period: int = 20,
entry_threshold: float = 0.002,
exit_threshold: float = 0.001,
position_size: float = 0.1
) -> BacktestResult:
"""
动量策略回测
逻辑:
- 当价格变化超过 entry_threshold 时入场
- 当反向变化超过 exit_threshold 时平仓
"""
df = df.sort_values("timestamp").reset_index(drop=True)
# 计算动量指标
df["returns"] = df["price"].pct_change()
df["momentum"] = df["returns"].rolling(lookback_period).sum()
in_position = False
entry_price = 0.0
for idx, row in df.iterrows():
if idx < lookback_period:
continue
equity = self.calculate_equity(row["price"])
self.equity_curve.append(equity)
if not in_position:
# 尝试入场
if row["momentum"] > entry_threshold:
size = (self.cash * position_size) / row["price"]
order = self.execute_buy(
row["timestamp"],
row["price"],
size
)
if order:
in_position = True
entry_price = row["price"]
else:
# 尝试平仓
pnl_pct = (row["price"] - entry_price) / entry_price
if pnl_pct < -exit_threshold or pnl_pct > exit_threshold * 3:
order = self.execute_sell(
row["timestamp"],
row["price"],
self.position
)
if order:
in_position = False
entry_price = 0.0
# 平掉剩余仓位
if in_position and self.position > 0:
last_price = df.iloc[-1]["price"]
self.execute_sell(df.iloc[-1]["timestamp"], last_price, self.position)
return self._generate_results()
def _generate_results(self) -> BacktestResult:
"""生成回测报告"""
result = BacktestResult()
# 计算交易统计
buy_trades = [t for t in self.trades if t.side == "BUY"]
sell_trades = [t for t in self.trades if t.side == "SELL"]
result.total_trades = len(buy_trades)
# 计算每笔交易的盈亏
for i in range(min(len(buy_trades), len(sell_trades))):
buy_price = buy_trades[i].price
sell_price = sell_trades[i].price
pnl = (sell_price - buy_price) * buy_trades[i].quantity
result.trades.append({
"buy_price": buy_price,
"sell_price": sell_price,
"pnl": pnl,
"pnl_pct": pnl / (buy_price * buy_trades[i].quantity)
})
if pnl > 0:
result.winning_trades += 1
result.avg_win += pnl
else:
result.losing_trades += 1
result.avg_loss += pnl
result.total_pnl += pnl
# 计算比率
if result.winning_trades > 0:
result.avg_win /= result.winning_trades
if result.losing_trades > 0:
result.avg_loss /= result.losing_trades
if result.total_trades > 0:
result.win_rate = result.winning_trades / result.total_trades
# 计算最大回撤
equity = np.array(self.equity_curve)
peak = np.maximum.accumulate(equity)
drawdown = (equity - peak) / peak
result.max_drawdown = abs(drawdown.min())
# 计算夏普比率(简化版)
returns = np.diff(equity) / equity[:-1]
if len(returns) > 0 and np.std(returns) > 0:
result.sharpe_ratio = np.mean(returns) / np.std(returns) * np.sqrt(252)
return result
def print_report(self, result: BacktestResult):
"""打印回测报告"""
print("\n" + "="*50)
print(" 回 测 报 告")
print("="*50)
print(f"初始资金: ${self.initial_capital:,.2f}")
print(f"总交易次数: {result.total_trades}")
print(f"盈利交易: {result.winning_trades}")
print(f"亏损交易: {result.losing_trades}")
print(f"胜率: {result.win_rate:.2%}")
print(f"总盈亏: ${result.total_pnl:,.2f}")
print(f"平均盈利: ${result.avg_win:,.2f}")
print(f"平均亏损: ${result.avg_loss:,.2f}")
print(f"最大回撤: {result.max_drawdown:.2%}")
print(f"夏普比率: {result.sharpe_ratio:.2f}")
print(f"最终权益: ${self.equity_curve[-1] if self.equity_curve else 0:,.2f}")
print("="*50)
使用示例
if __name__ == "__main__":
# 加载历史数据
processor = TradeDataProcessor("btc_trades.db")
df = processor.load_from_database("BTCUSDT")
if len(df) > 0:
# 运行回测
backtester = TickBacktester(
initial_capital=100000,
commission=0.0004,
slippage=0.0001
)
result = backtester.run_momentum_strategy(
df,
lookback_period=50,
entry_threshold=0.003,
exit_threshold=0.002,
position_size=0.2
)
backtester.print_report(result)
第四部分:高级 Tick 数据分析
"""
高级成交数据分析
包含订单流、VPIN 和冰山订单检测
"""
import pandas as pd
import numpy as np
from collections import deque
class TickAnalyzer:
"""Tick 数据高级分析"""
def __init__(self, window_size: int = 1000):
self.window_size = window_size
self.volume_buckets = deque(maxlen=window_size)
self.trade_sizes = deque(maxlen=window_size)
def calculate_vpin(
self,
df: pd.DataFrame,
bucket_size: int = 50
) -> pd.Series:
"""
计算 Volume-synchronized Probability of Informed Trading (VPIN)
VPIN 用于检测流动性异常和潜在的价格操纵
"""
df = df.sort_values("timestamp").reset_index(drop=True)
# 分类买入/卖出交易量
df["buy_volume"] = np.where(
~df["is_buyer_maker"],
df["quantity"],
0
)
df["sell_volume"] = np.where(
df["is_buyer_maker"],
df["quantity"],
0
)
# 按成交量分桶
df["volume_bucket"] = (df["quantity"].cumsum() / bucket_size).astype(int)
# 计算每个桶的 VPIN
vpin_data = []
for bucket in df["volume_bucket"].unique():
bucket_df = df[df["volume_bucket"] == bucket]
buy_vol = bucket_df["buy_volume"].sum()
sell_vol = bucket_df["sell_volume"].sum()
total_vol = buy_vol + sell_vol
if total_vol > 0:
vpin = abs(buy_vol - sell_vol) / total_vol
vpin_data.append({
"bucket": bucket,
"vpin": vpin,
"timestamp": bucket_df["timestamp"].iloc[0]
})
vpin_df = pd.DataFrame(vpin_data)
if not vpin_df.empty:
# 添加移动平均
vpin_df["vpin_ma"] = vpin_df["vpin"].rolling(10).mean()
return vpin_df.set_index("bucket")["vpin"]
def detect_iceberg_orders(
self,
df: pd.DataFrame,
threshold: float = 0.01
) -> pd.DataFrame:
"""
检测冰山订单
冰山订单特征:大量小单中间穿插少量大单
"""
df = df.sort_values("timestamp").reset_index(drop=True)
# 计算成交量的滚动统计
df["volume_ma"] = df["quantity"].rolling(20).mean()
df["volume_std"] = df["quantity"].rolling(20).std()
df["volume_zscore"] = (df["quantity"] - df["volume_ma"]) / df["volume_std"]
# 识别异常大单
df["iceberg_flag"] = df["volume_zscore"] > 3
# 识别冰山模式:小单中间穿插大单
iceberg_patterns = []
for i in range(len(df) - 2):
if df.iloc[i]["quantity"] < df["quantity"].quantile(threshold) and \
df.iloc[i+1]["quantity"] > df["quantity"].quantile(1-threshold) and \
df.iloc[i+2]["quantity"] < df["quantity"].quantile(threshold):
iceberg_patterns.append({
"timestamp": df.iloc[i+1]["timestamp"],
"hidden_size": df.iloc[i+1]["quantity"],
"displayed_size": min(df.iloc[i]["quantity"], df.iloc[i+2]["quantity"]),
"price": df.iloc[i+1]["price"]
})
return pd.DataFrame(iceberg_patterns)
def calculate_order_flow_imbalance(
self,
df: pd.DataFrame,
window: int = 100
) -> pd.Series:
"""
计算订单流不平衡 (Order Flow Imbalance)
正值表示净买入压力,负值表示净卖出压力
"""
df = df.sort_values("timestamp").reset_index(drop=True)
# 标记买卖方向
df["signed_volume"] = np.where(
~df["is_buyer_maker"],
df["quantity"],
-df["quantity"]
)
# 计算滚动 OFI
ofi = df["signed_volume"].rolling(window).sum()
# 标准化
ofi_normalized = (ofi - ofi.mean()) / ofi.std()
return ofi_normalized
def detect_large_trades(
self,
df: pd.DataFrame,
percentile: float = 99
) -> pd.DataFrame:
"""
检测大额交易
用于识别机构活动和市场推动因素
"""
threshold = df["quote_quantity"].quantile(percentile / 100)
large_trades = df[df["quote_quantity"] >= threshold].copy()
# 分类大额交易
large_trades["trade_category"] = pd.cut(
large_trades["quote_quantity"],
bins=[threshold, threshold*5, threshold*20, np.inf],
labels=["Large", "Very Large", "Whale"]
)
return large_trades
def calculate_price_impact(
self,
df: pd.DataFrame,
trade_window: int = 10
) -> pd.DataFrame:
"""
计算每笔交易对价格的冲击
用于评估流动性成本
"""
df = df.sort_values("timestamp").reset_index(drop=True)
# 计算交易前后的价格
df["price_before"] = df["price"].shift(1)
df["price_after"] = df["price"].shift(-trade_window)
# 计算价格冲击
df["price_impact"] = (
df["price_after"] - df["price_before"]
) / df["price_before"]
# 考虑交易方向
df["signed_impact"] = np.where(
~df["is_buyer_maker"],
df["price_impact"],
-df["price_impact"]
)
return df.dropna()
使用示例
if __name__ == "__main__":
processor = TradeDataProcessor("btc_trades.db")
df = processor.load_from_database("BTCUSDT")
if len(df) > 100:
analyzer = TickAnalyzer()
# 计算 VPIN
vpin = analyzer.calculate_vpin(df)
print("VPIN 统计:")
print(vpin.describe())
# 检测冰山订单
icebergs = analyzer.detect_iceberg_orders(df)
print(f"\n检测到 {len(icebergs)} 个冰山订单模式")
# 检测大额交易
whales = analyzer.detect_large_trades(df)
print(f"\n检测到 {len(whales)} 笔大额交易")
# 计算价格冲击
impact_df = analyzer.calculate_price_impact(df)
print(f"\n平均价格冲击: {impact_df['price_impact'].mean():.6f}")
Preise und ROI
| 套餐 | Preis | 包含 Credits | 适用场景 | ROI 分析 |
|---|---|---|---|---|
| 免费版 | ¥0 / $0 | 注册即送 | 学习和测试 | 零成本入门 |
| Starter | ¥49 / ~$7 | ~15M Tokens | 个人量化研究 | 1 个月开发测试 |
| Pro | ¥199 / ~$28 | ~65M Tokens | 专业交易策略 | vs 官方 $420+,省 $390+ |
| Enterprise | 定制 | 无限量 | 机构级量化 | vs 官方节省 85%+ |
与官方 Binance API 成本对比(以 100M Tokens 计算)
- 官方 API(高级套餐):约 $280-400 / 月
- HolySheep AI(Pro 套餐):约 $28 / 月
- 年度节省:超过 $3,000(相比官方)
- 数据质量:全量历史 Tick Data,无采样
Häufige Fehler und Lösungen
错误 1:时间戳格式错误导致数据缺失
问题描述:请求返回空数据或 400 错误。
# ❌ 错误:使用秒级时间戳
start_ts = int(datetime(2024, 1, 1).timestamp()) # 秒
✅ 正确:使用毫秒级时间戳
start_ts = int(datetime(2024, 1, 1).timestamp() * 1000) # 毫秒
或者使用字符串格式
api.get_historical_trades(
symbol="BTCUSDT",
start_time=1704067200000, # 毫秒
end_time=1704153600000
)
错误 2:速率限制触发导致请求失败
问题描述:返回 429 Too Many Requests 错误。
# ❌ 错误:连续快速请求
for symbol in symbols:
trades = api.get_historical_trades(symbol) # 容易触发限流
✅ 正确:添加延迟和指数退避
import time
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
class RateLimitedAPI:
def __init__(self, api_key):
self.api = CryptoTradeDataAPI(api_key)
self.session = requests.Session()
# 配置重试策略
retry_strategy = Retry(
total=3,
backoff_factor=1,
status_forcelist=[429, 500, 502, 503, 504]
)
adapter = HTTPAdapter(max_retries=retry_strategy)
self.session.mount("https://", adapter)
def safe_request(self, symbol, **kwargs):
try:
result = self.api.get_historical_trades(symbol, **kwargs)
time.sleep(0.5) # 请求间隔 500ms
return result
except Exception as e:
print(f"请求失败,5秒后重试: {e}")
time.sleep(5)
return self.api.get_historical_trades(symbol, **kwargs)
错误 3:浮点数精度导致计算错误
问题描述:回测结果与实盘收益差异大。
# ❌ 错误:直接使用浮点数计算
price = 0.00000001 # 小数精度问题
quantity = 100.123456789
✅ 正确:使用 Decimal 精确计算
from decimal import Decimal, ROUND_DOWN
def calculate_trade_cost(
price: float,
quantity: float,
commission_rate: float = 0.0004
) -> dict:
price_dec = Decimal(str(price))
qty_dec = Decimal(str(quantity))
comm_dec = Decimal(str(commission_rate))
# 计算成交额(精确到价格精度)
quote = (price_dec * qty_dec).quantize(
Decimal('0.00000001'),
rounding=ROUND_DOWN
)
# 计算手续费
fee = (quote * comm_dec).quantize(
Decimal('0.00000001'),
rounding=ROUND_DOWN
)
return {
"quote": float(quote),
"fee": float(fee),
"total_cost": float(quote + fee)
}
使用示例
result = calculate_trade_cost(0.000015678, 15000.5)
print(f"成交额: {result['quote']}, 手续费: {result['fee']}")
错误 4:数据重复或丢失
问题描述:回测结果不稳定,相同代码多次运行结果不同。
# ❌ 错误:直接追加数据,可能导致重复
def save_trades_unSAFE(trades, db_path):
df = pd.DataFrame(trades)
df.to_sql("trades", sqlite3.connect(db_path), if_exists="append")
✅ 正确:先检查再插入,使用事务
def save_trades_safe(trades, db_path):
if not trades:
return
df = pd.DataFrame(trades)
df = df.drop