การทำ backtest ระบบเทรดที่แม่นยำนั้นต้องอาศัยข้อมูล order book คุณภาพสูง หลายคนเริ่มต้นใช้ Tardis API แล้วเจอปัญหาเรื่อง data quality ที่ทำให้ผล backtest ไม่ตรงกับความเป็นจริง บทความนี้จะพาคุณตรวจสอบคุณภาพข้อมูลตั้งแต่逐笔成交 (逐笔成交 - ข้อมูลการซื้อขายทีละรายการ) ไปจนถึง盘口快照 (盘口快照 - สแน็ปช็อตราคาซื้อขาย) อย่างเป็นระบบ พร้อมแนะนำทางเลือกที่คุ้มค่ากว่าสำหรับนักพัฒนาที่ต้องการประหยัดต้นทุน API ถึง 85%
Tardis API คืออะไร และทำไมต้องสนใจคุณภาพข้อมูล
Tardis เป็นบริการที่รวบรวมข้อมูลการซื้อขายสินทรัพย์ดิจิทัลจากหลาย exchange รวมถึง Binance, Bybit และ OKX บริการนี้ให้บริการข้อมูล order book historical ซึ่งเป็นสิ่งจำเป็นสำหรับการทำ quantitative trading, การวิเคราะห์ market microstructure และการพัฒนา trading bot
อย่างไรก็ตาม ข้อมูลที่ได้มาจาก API มักมีปัญหาที่ซ่อนอยู่ เช่น ข้อมูลหาย ความไม่สอดคล้องกันระหว่าง tick data กับ snapshot และความล่าช้าของข้อมูล (latency) การตรวจสอบคุณภาพข้อมูลก่อนนำไปใช้จึงเป็นขั้นตอนที่ห้ามมองข้าม
ประเภทข้อมูล Order Book ที่ควรรู้
1. ข้อมูล逐笔成交 (Tick Data / Trade Data)
ข้อมูลประเภทนี้บันทึกทุกการซื้อขายที่เกิดขึ้นในตลาด รวมถึง ราคา ปริมาณ เวลาที่แน่นอน และฝ่ายที่เป็นผู้ซื้อหรือผู้ขาย (taker side) ข้อมูลนี้สำคัญมากสำหรับการคำนวณ volume profile, order flow analysis และการหา market impact
2. ข้อมูล盘口快照 (Order Book Snapshot)
สแน็ปช็อตของ order book ณ เวลาใดเวลาหนึ่ง แสดงรายการคำสั่งซื้อที่รอดำเนินการ (pending orders) ทั้ง bid และ ask ข้อมูลนี้ใช้สำหรับคำนวณ order book imbalance, depth of market และ liquidation levels
3. ข้อมูล Incremental Updates (Delta Updates)
การอัปเดตที่เกิดขึ้นระหว่างสแน็ปช็อต ช่วยให้สามารถ reconstruct order book ได้ตลอดเวลาโดยไม่ต้องดึงข้อมูล snapshot บ่อยๆ
ข้อมูลที่ต้องเตรียมก่อนเริ่มตรวจสอบ
สำหรับบทความนี้ ผู้เขียนใช้งาน Tardis API จริงในโปรเจกต์ backtest ระบบ scalping บน Binance futures เป็นเวลา 6 เดือน พบปัญหาคุณภาพข้อมูลหลายจุดที่ต้องแก้ไขก่อนจะได้ผล backtest ที่น่าเชื่อถือ
- Tardis API key (สามารถสมัครทดลองใช้งานได้)
- Python 3.9 ขึ้นไป
- ไลบรารี requests และ pandas
- ข้อมูล pair ที่ต้องการตรวจสอบ (เช่น BTCUSDT perpetual)
- ช่วงเวลาที่ต้องการตรวจสอบ
ขั้นตอนที่ 1: ดึงข้อมูล Tick Data จาก Tardis API
เริ่มต้นด้วยการดึงข้อมูล trade data หรือ逐笔成交 มาตรวจสอบกันก่อน ด้านล่างคือโค้ด Python สำหรับเริ่มต้นดึงข้อมูล:
import requests
import pandas as pd
from datetime import datetime
ตั้งค่าพารามิเตอร์
exchange = "binance"
market = "futures"
symbol = "BTCUSDT"
data_type = "trades" # ข้อมูลการซื้อขาย
กำหนดช่วงเวลา (ตัวอย่าง: 1 ชั่วโมง)
start_date = "2026-05-01T00:00:00Z"
end_date = "2026-05-01T01:00:00Z"
Tardis API endpoint
url = f"https://api.tardis.dev/v1/historical/{exchange}/{market}/{data_type}"
params = {
"symbol": symbol,
"from": start_date,
"to": end_date,
"format": "json"
}
headers = {
"Authorization": "Bearer YOUR_TARDIS_API_KEY"
}
ดึงข้อมูล
response = requests.get(url, headers=headers, params=params)
trades_data = response.json()
print(f"จำนวน trades ที่ได้รับ: {len(trades_data)}")
print("ตัวอย่างข้อมูล 5 รายการแรก:")
print(trades_data[:5])
ขั้นตอนที่ 2: ตรวจสอบ Data Quality ของ Tick Data
หลังจากได้ข้อมูลมาแล้ว ต้องตรวจสอบคุณภาพตาม checklist ด้านล่าง:
import numpy as np
def check_tick_data_quality(trades_data):
"""ตรวจสอบคุณภาพข้อมูล tick data"""
df = pd.DataFrame(trades_data)
# แปลง timestamp
df['timestamp'] = pd.to_datetime(df['timestamp'])
issues = []
# 1. ตรวจสอบค่าว่าง
null_counts = df.isnull().sum()
if null_counts.any():
issues.append(f"พบค่าว่างใน columns: {null_counts[null_counts > 0].to_dict()}")
# 2. ตรวจสอบ timestamp ที่ขาดหาย
df_sorted = df.sort_values('timestamp')
time_diffs = df_sorted['timestamp'].diff()
# หา gaps ที่ผิดปกติ (มากกว่า 1 นาที)
large_gaps = time_diffs[time_diffs > pd.Timedelta(minutes=1)]
if len(large_gaps) > 0:
issues.append(f"พบ {len(large_gaps)} จุดที่ข้อมูลหาย (gap > 1 นาที)")
print("ตำแหน่งที่พบ gap:")
for idx in large_gaps.index[:5]: # แสดง 5 จุดแรก
gap_size = time_diffs[idx]
print(f" ก่อน {idx}: gap = {gap_size}")
# 3. ตรวจสอบราคาที่ผิดปกติ
price_stats = df['price'].describe()
q1 = price_stats['25%']
q3 = price_stats['75%']
iqr = q3 - q1
lower_bound = q1 - 3 * iqr
upper_bound = q3 + 3 * iqr
outliers = df[(df['price'] < lower_bound) | (df['price'] > upper_bound)]
if len(outliers) > 0:
issues.append(f"พบ {len(outliers)} รายการที่ราคาผิดปกติ (outliers)")
print("ตัวอย่าง outliers:")
print(outliers.head())
# 4. ตรวจสอบ volume ที่เป็นลบ
negative_volumes = df[df['volume'] <= 0]
if len(negative_volumes) > 0:
issues.append(f"พบ {len(negative_volumes)} รายการที่ volume <= 0")
# 5. ตรวจสอบ side (buy/sell) ที่ไม่ถูกต้อง
invalid_sides = df[~df['side'].isin(['buy', 'sell'])]
if len(invalid_sides) > 0:
issues.append(f"พบ {len(invalid_sides)} รายการที่ side ไม่ถูกต้อง")
# 6. ตรวจสอบความสมบูรณ์ของข้อมูล
total_expected = len(df)
data_coverage = (total_expected - len(large_gaps)) / total_expected * 100
print(f"\n📊 Data Coverage: {data_coverage:.2f}%")
if issues:
print("\n⚠️ ปัญหาที่พบ:")
for issue in issues:
print(f" - {issue}")
return False, issues
else:
print("\n✅ ข้อมูล tick data ผ่านการตรวจสอบเบื้องต้น")
return True, []
รันการตรวจสอบ
is_valid, issues = check_tick_data_quality(trades_data)
ขั้นตอนที่ 3: ดึงและตรวจสอบ Order Book Snapshot
ขั้นตอนต่อไปคือดึงข้อมูล order book snapshot หรือ盘口快照 มาตรวจสอบ:
# ดึงข้อมูล order book snapshot
snapshot_url = f"https://api.tardis.dev/v1/historical/{exchange}/{market}/order-books-snapshots"
snapshot_params = {
"symbol": symbol,
"from": start_date,
"to": end_date,
"format": "json"
}
response = requests.get(snapshot_url, headers=headers, params=snapshot_params)
snapshots_data = response.json()
print(f"จำนวน snapshots ที่ได้รับ: {len(snapshots_data)}")
def check_snapshot_quality(snapshots_data):
"""ตรวจสอบคุณภาพข้อมูล order book snapshot"""
issues = []
valid_snapshots = []
for snapshot in snapshots_data:
snapshot_issues = []
# 1. ตรวจสอบโครงสร้างข้อมูล
if 'asks' not in snapshot or 'bids' not in snapshot:
snapshot_issues.append("ไม่มี asks หรือ bids")
issues.append({"timestamp": snapshot.get('timestamp'), "issues": snapshot_issues})
continue
# 2. ตรวจสอบ asks > bids (ราคาซื้อต่ำกว่าราคาขายเสมอ)
best_ask = float(snapshot['asks'][0][0]) if snapshot['asks'] else float('inf')
best_bid = float(snapshot['bids'][0][0]) if snapshot['bids'] else 0
if best_ask <= best_bid:
snapshot_issues.append(f"Best ask ({best_ask}) <= Best bid ({best_bid})")
# 3. ตรวจสอบ spread ที่ผิดปกติ
spread = best_ask - best_bid
spread_pct = spread / best_bid * 100 if best_bid > 0 else 0
if spread_pct > 0.1: # spread เกิน 0.1%
snapshot_issues.append(f"Spread สูงผิดปกติ: {spread_pct:.4f}%")
# 4. ตรวจสอบจำนวน levels
if len(snapshot['asks']) < 10 or len(snapshot['bids']) < 10:
snapshot_issues.append(f"จำนวน levels น้อย (asks: {len(snapshot['asks'])}, bids: {len(snapshot['bids'])})")
# 5. ตรวจสอบราคาที่เรียงลำดับถูกต้อง
asks_prices = [float(x[0]) for x in snapshot['asks']]
bids_prices = [float(x[0]) for x in snapshot['bids']]
if asks_prices != sorted(asks_prices):
snapshot_issues.append("asks ไม่เรียงลำดับจากต่ำไปสูง")
if bids_prices != sorted(bids_prices, reverse=True):
snapshot_issues.append("bids ไม่เรียงลำดับจากสูงไปต่ำ")
# 6. ตรวจสอบ volume ที่เป็นลบ
for level in snapshot['asks'] + snapshot['bids']:
if float(level[1]) <= 0:
snapshot_issues.append("พบ volume ที่ <= 0")
break
if snapshot_issues:
issues.append({"timestamp": snapshot.get('timestamp'), "issues": snapshot_issues})
else:
valid_snapshots.append(snapshot)
total = len(snapshots_data)
valid = len(valid_snapshots)
valid_pct = valid / total * 100 if total > 0 else 0
print(f"\n📊 Snapshot Quality:")
print(f" ทั้งหมด: {total}")
print(f" ผ่าน: {valid} ({valid_pct:.1f}%)")
print(f" มีปัญหา: {len(issues)}")
return valid_snapshots, issues
valid_snapshots, snapshot_issues = check_snapshot_quality(snapshots_data)
ขั้นตอนที่ 4: Cross-Validation ระหว่าง Tick Data และ Snapshot
ขั้นตอนสำคัญที่หลายคนมองข้ามคือการตรวจสอบความสอดคล้องกันระหว่าง tick data กับ snapshot นี่คือสิ่งที่ต้องทำ:
def cross_validate_tick_and_snapshot(trades_data, snapshots_data, symbol):
"""
Cross-validate ว่า tick data ตรงกับ snapshot
วิธีการ: ตรวจสอบว่าราคาของ trade แรกในแต่ละช่วง snapshot
อยู่ในช่วง bid-ask ของ snapshot นั้นๆ
"""
issues = []
validations = []
# แปลง timestamp ให้เป็น datetime
trades_df = pd.DataFrame(trades_data)
trades_df['timestamp'] = pd.to_datetime(trades_df['timestamp'])
trades_df = trades_df.sort_values('timestamp')
for i, snapshot in enumerate(snapshots_data):
snapshot_ts = pd.to_datetime(snapshot['timestamp'])
# หา trades ที่อยู่ในช่วงของ snapshot นี้
# snapshot ถัดไปจะเป็นตัวกำหนดจุดสิ้นสุด
if i + 1 < len(snapshots_data):
next_snapshot_ts = pd.to_datetime(snapshots_data[i + 1]['timestamp'])
else:
next_snapshot_ts = snapshot_ts + pd.Timedelta(minutes=1) # สมมติ 1 นาที
# กรอง trades ในช่วงนี้
trades_in_range = trades_df[
(trades_df['timestamp'] >= snapshot_ts) &
(trades_df['timestamp'] < next_snapshot_ts)
]
# ดึง best bid/ask จาก snapshot
best_bid = float(snapshot['bids'][0][0]) if snapshot['bids'] else 0
best_ask = float(snapshot['asks'][0][0]) if snapshot['asks'] else float('inf')
# ตรวจสอบแต่ละ trade
trades_outside_spread = 0
for _, trade in trades_in_range.iterrows():
price = float(trade['price'])
if price < best_bid or price > best_ask:
trades_outside_spread += 1
validation = {
"snapshot_ts": snapshot_ts,
"trade_count": len(trades_in_range),
"trades_outside_spread": trades_outside_spread,
"best_bid": best_bid,
"best_ask": best_ask,
"is_valid": trades_outside_spread == 0
}
validations.append(validation)
if trades_outside_spread > 0:
issues.append({
"timestamp": snapshot_ts,
"outside_spread": trades_outside_spread,
"total_trades": len(trades_in_range)
})
# สรุปผล
valid_count = sum(1 for v in validations if v['is_valid'])
print(f"\n📊 Cross-Validation Results:")
print(f" ทั้งหมด: {len(validations)} snapshots")
print(f" ผ่าน: {valid_count} ({valid_count/len(validations)*100:.1f}%)")
print(f" มีปัญหา: {len(issues)}")
if issues:
print("\n⚠️ ตัวอย่างปัญหา:")
for issue in issues[:3]:
print(f" {issue['timestamp']}: {issue['outside_spread']}/{issue['total_trades']} trades อยู่นอก spread")
return validations, issues
รัน cross-validation
validations, cross_issues = cross_validate_tick_and_snapshot(trades_data, snapshots_data, symbol)
ขั้นตอนที่ 5: สร้างรายงาน Data Quality Report
หลังจากตรวจสอบทุกอย่างแล้ว ให้สร้างรายงานสรุปเพื่อใช้ในการตัดสินใจว่าจะใช้ข้อมูลชุดนี้หรือไม่:
def generate_data_quality_report(
tick_data_issues,
snapshot_issues,
cross_validation_issues,
trades_data,
snapshots_data
):
"""สร้างรายงานสรุปคุณภาพข้อมูล"""
report = {
"data_coverage": {
"trades_count": len(trades_data),
"snapshots_count": len(snapshots_data),
"time_range": f"{trades_data[0]['timestamp']} to {trades_data[-1]['timestamp']}"
},
"tick_data_health": {
"issues_count": len(tick_data_issues),
"status": "✅ PASS" if len(tick_data_issues) == 0 else "⚠️ NEED REVIEW"
},
"snapshot_health": {
"total": len(snapshots_data),
"valid": len(snapshots_data) - len(snapshot_issues),
"issues_count": len(snapshot_issues),
"status": "✅ PASS" if len(snapshot_issues) == 0 else "⚠️ NEED REVIEW"
},
"cross_validation": {
"issues_count": len(cross_validation_issues),
"status": "✅ PASS" if len(cross_validation_issues) == 0 else "❌ FAIL"
},
"recommendation": None
}
# คำนวณ overall score
total_issues = (
len(tick_data_issues) +
len(snapshot_issues) +
len(cross_validation_issues)
)
max_allowed_issues = len(snapshots_data) * 0.05 # ยอมรับได้ 5% error
if total_issues == 0:
report["recommendation"] = "✅ ข้อมูลพร้อมใช้งาน"
report["quality_score"] = 100
elif total_issues <= max_allowed_issues:
report["recommendation"] = "⚠️ ใช้งานได้แต่ควรระวัง (filter outliers)"
report["quality_score"] = 80
elif total_issues <= len(snapshots_data) * 0.2:
report["recommendation"] = "⚠️ ต้อง clean data ก่อนใช้งาน"
report["quality_score"] = 60
else:
report["recommendation"] = "❌ ไม่แนะนำให้ใช้ข้อมูลชุดนี้"
report["quality_score"] = 40
return report
สร้างรายงาน
report = generate_data_quality_report(
issues, # tick data issues
snapshot_issues,
cross_issues,
trades_data,
snapshots_data
)
print("=" * 60)
print("📋 DATA QUALITY REPORT")
print("=" * 60)
print(f"⏰ Time Range: {report['data_coverage']['time_range']}")
print(f"📊 Trades: {report['data_coverage']['trades_count']:,}")
print(f"📊 Snapshots: {report['data_coverage']['snapshots_count']:,}")
print("-" * 60)
print(f"Tick Data Health: {report['tick_data_health']['status']}")
print(f"Snapshot Health: {report['snapshot_health']['status']}")
print(f"Cross Validation: {report['cross_validation']['status']}")
print("-" * 60)
print(f"🎯 Quality Score: {report['quality_score']}/100")
print(f"📝 Recommendation: {report['recommendation']}")
print("=" * 60)
วิธีใช้งานข้อมูลที่ผ่านการตรวจสอบใน Backtest
หลังจากได้ข้อมูลที่ผ่านการตรวจสอบแล้ว มาดูวิธีนำไปใช้ในระบบ backtest:
def prepare_backtest_data(trades_data, snapshots_data, quality_threshold=80):
"""
เตรียมข้อมูลสำหรับ backtest
กรองเฉพาะข้อมูลที่ผ่านเกณฑ์คุณภาพ
"""
# แปลงเป็น DataFrame
trades_df = pd.DataFrame(trades_data)
trades_df['timestamp'] = pd.to_datetime(trades_df['timestamp'])
# คำนวณ rolling statistics เพื่อหา outliers
trades_df['price_ma'] = trades_df['price'].rolling(window=20).mean()
trades_df['price_std'] = trades_df['price'].rolling(window=20).std()
trades_df['price_zscore'] = (
(trades_df['price'] - trades_df['price_ma']) / trades_df['price_std']
)
# กรอง outliers (z-score > 3 หรือ < -3)
clean_trades = trades_df[
(trades_df['price_zscore'].abs() < 3) |
(trades_df['price_zscore'].isna())
].copy()
# กรอง trades ที่มี volume ต่ำผิดปกติ
volume_median = clean_trades['volume'].median()
clean_trades = clean_trades[clean_trades['volume'] >= volume_median * 0.01]
# เตรียม order book data
orderbook_data = []
for snapshot in snapshots_data:
orderbook_data.append({
'timestamp': snapshot['timestamp'],
'bids': [(float(p), float(q)) for p, q in snapshot['bids']],
'asks': [(float(p), float(q)) for p, q in snapshot['asks']],
'best_bid': float(snapshot['bids'][0][0]),
'best_ask': float(snapshot['asks'][0][0]),
'mid_price': (
float(snapshot['bids'][0][0]) + float(snapshot['asks'][0][0])
) / 2,
'spread': (
float(snapshot['asks'][0][0]) - float(snapshot['bids'][0][0])
)
})
orderbook_df = pd.DataFrame(orderbook_data)
return {
'trades': clean_trades,
'orderbook': orderbook_df,
'stats': {
'original_trades': len(trades_df),
'clean_trades': len(clean_trades),
'clean_rate': len(clean_trades) / len(trades_df) * 100
}
}
เตรียมข้อมูลสำหรับ backtest
backtest_data = prepare_backtest_data(trades_data, snapshots_data)
print(f"📊 Data Preparation:")
print(f" Original: {backtest_data['stats']['original_trades']:,} trades")
print(f" Clean: {backtest_data['stats']['clean_trades']:,} trades")
print(f" Clean Rate: {backtest_data['stats']['clean_rate']:.1f}%")
ทางเลือกที่คุ้มค่ากว่า: HolySheep AI API
ระหว่างที่ใช้งาน Tardis API ผู้เขียนค้นพบ HolySheep AI ซึ่งเป็นแพลตฟอร์ม AI API ที่มีข้อได้เปรียบด้านราคาอย่างมาก โดยเฉพาะสำหรับนักพัฒนาที่ต้องการใช้ AI model สำหรับ data analysis และ signal generation
HolySheep AI มีอัตราแลกเปลี่ยนที่คุ้มค่ามาก ¥1 = $1 ซึ่งประหยัดได้ถึง 85% เมื่อเทียบกับบริการอื่นๆ นอกจากนี้ยังรองรับ WeChat และ Alipay ทำให้สะดวกสำหรับผู้ใช้ในประเทศจีน ระบบมีความ