การทำ Quantitative Trading โดยเฉพาะอย่างยิ่งการ Backtest กลยุทธ์ที่เราออกแบบนั้น จำเป็นต้องมีข้อมูล Orderbook ย้อนหลังที่มีความถูกต้องและครบถ้วน หลายคนมักประสบปัญหาในการเข้าถึงข้อมูล Historical Orderbook ของ Binance วันนี้เราจะมาดูวิธีการเข้าถึงข้อมูลเหล่านี้อย่างละเอียด พร้อมโค้ดตัวอย่างที่พร้อมใช้งานจริง
ทำไมต้องใช้ข้อมูล Historical Orderbook?
ข้อมูล Orderbook เป็นหัวใจสำคัญในการวิเคราะห์ตลาดและออกแบบกลยุทธ์การเทรด เพราะมันบอกรายละเอียดเกี่ยวกับ:
- ความลึกของตลาด (Market Depth) และสภาพคล่อง
- แนวโน้มการสั่งซื้อ-ขายของนักลงทุนรายใหญ่
- จุดที่อาจเกิดแรงกดดันด้านราคา (Support/Resistance)
- ความผันผวนและปริมาณการซื้อขายในช่วงเวลาต่างๆ
วิธีที่ 1: ใช้ Binance API โดยตรง
Binance มี Public API สำหรับดึงข้อมูล Historical Orderbook ผ่าน endpoint /api/v3/depth แต่ต้องระวังว่า Binance ไม่เก็บข้อมูล Orderbook ย้อนหลังไว้นาน ปกติจะเก็บเพียงไม่กี่วันเท่านั้น
ติดตั้งและใช้งาน Binance Connector
pip install binance-connector pandas numpy
import time
import pandas as pd
from binance.spot import Spot as Client
from binance.error import ClientError
class BinanceOrderbookFetcher:
"""คลาสสำหรับดึงข้อมูล Historical Orderbook จาก Binance"""
def __init__(self, symbol='BTCUSDT', limit=1000):
self.client = Client(base_url='https://api.binance.com')
self.symbol = symbol.upper()
self.limit = limit # จำนวนระดับราคาที่ต้องการ (1-5000)
def get_orderbook_snapshot(self, timestamp=None):
"""
ดึงข้อมูล Orderbook Snapshot ณ ปัจจุบัน
ข้อมูลจะถูกจำกัดไว้ที่ 500 ระดับราคาต่อครั้ง
"""
try:
params = {
'symbol': self.symbol,
'limit': min(self.limit, 1000) # สูงสุด 1000 ระดับ
}
response = self.client.depth(**params)
orderbook_data = {
'timestamp': response.get('lastUpdateId'),
'bids': response.get('bids', []),
'asks': response.get('asks', []),
'lastUpdateId': response.get('lastUpdateId')
}
return orderbook_data
except ClientError as error:
print(f"❌ Client Error: {error.error_code} - {error.error_message}")
return None
def get_orderbook_dataframe(self):
"""แปลงข้อมูล Orderbook เป็น DataFrame สำหรับวิเคราะห์"""
orderbook = self.get_orderbook_snapshot()
if orderbook is None:
return None
# สร้าง DataFrame สำหรับ Bids (คำสั่งซื้อ)
bids_df = pd.DataFrame(orderbook['bids'],
columns=['price', 'quantity'],
dtype=float)
# สร้าง DataFrame สำหรับ Asks (คำสั่งขาย)
asks_df = pd.DataFrame(orderbook['asks'],
columns=['price', 'quantity'],
dtype=float)
return {
'timestamp': orderbook['timestamp'],
'bids': bids_df,
'asks': asks_df
}
def calculate_market_depth(self, levels=10):
"""คำนวณ Market Depth สำหรับการวิเคราะห์"""
data = self.get_orderbook_dataframe()
if data is None:
return None
bids = data['bids'].head(levels)
asks = data['asks'].head(levels)
total_bid_volume = (bids['price'] * bids['quantity']).sum()
total_ask_volume = (asks['price'] * asks['quantity']).sum()
return {
'total_bid_volume': total_bid_volume,
'total_ask_volume': total_ask_volume,
'bid_ask_ratio': total_bid_volume / total_ask_volume if total_ask_volume > 0 else 0,
'spread': float(asks['price'].min() - bids['price'].max()) if len(asks) > 0 and len(bids) > 0 else 0,
'spread_percentage': float((asks['price'].min() - bids['price'].max()) / asks['price'].min() * 100) if len(asks) > 0 and len(bids) > 0 else 0
}
ตัวอย่างการใช้งาน
fetcher = BinanceOrderbookFetcher(symbol='BTCUSDT', limit=500)
orderbook_data = fetcher.get_orderbook_dataframe()
if orderbook_data:
print(f"📊 ข้อมูล Orderbook ณ เวลา: {orderbook_data['timestamp']}")
print(f"\n💚 Top 5 Bids (คำสั่งซื้อ):")
print(orderbook_data['bids'].head())
print(f"\n❤️ Top 5 Asks (คำสั่งขาย):")
print(orderbook_data['asks'].head())
depth = fetcher.calculate_market_depth(levels=10)
print(f"\n📈 Market Depth Analysis:")
print(f" Total Bid Volume: ${depth['total_bid_volume']:,.2f}")
print(f" Total Ask Volume: ${depth['total_ask_volume']:,.2f}")
print(f" Bid/Ask Ratio: {depth['bid_ask_ratio']:.4f}")
print(f" Spread: ${depth['spread']:.2f} ({depth['spread_percentage']:.4f}%)")
วิธีที่ 2: ใช้ Binance WebSocket สำหรับ Real-time + Historical Data
สำหรับการทำ Backtest ที่ต้องการข้อมูลย้อนหลังหลายวัน วิธีที่ดีที่สุดคือการใช้ WebSocket ร่วมกับการเก็บข้อมูลอย่างต่อเนื่อง หรือใช้บริการจากผู้ให้บริการข้อมูล third-party
import websocket
import json
import pandas as pd
import threading
import sqlite3
from datetime import datetime, timedelta
import time
class BinanceOrderbookRecorder:
"""
คลาสสำหรับบันทึกข้อมูล Orderbook ผ่าน WebSocket
เหมาะสำหรับการเก็บข้อมูลย้อนหลังเพื่อใช้ใน Backtesting
"""
def __init__(self, symbol='btcusdt', db_path='orderbook_data.db'):
self.symbol = symbol.lower()
self.stream_url = f"wss://stream.binance.com:9443/ws/{self.symbol}@depth20@100ms"
self.db_path = db_path
self.is_running = False
self.recorded_data = []
self._setup_database()
def _setup_database(self):
"""สร้างตารางใน SQLite สำหรับเก็บข้อมูล"""
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS orderbook_snapshots (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp INTEGER,
datetime TEXT,
bid_price REAL,
bid_quantity REAL,
ask_price REAL,
ask_quantity REAL,
update_id INTEGER
)
''')
cursor.execute('''
CREATE INDEX IF NOT EXISTS idx_timestamp
ON orderbook_snapshots(timestamp)
''')
conn.commit()
conn.close()
print(f"✅ Database setup complete: {self.db_path}")
def on_message(self, ws, message):
"""จัดการเมื่อได้รับข้อความใหม่จาก WebSocket"""
try:
data = json.loads(message)
if 'bids' in data and 'asks' in data:
timestamp = data.get('lastUpdateId', int(time.time() * 1000))
dt = datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')
# บันทึก top 5 levels ของ orderbook
for i, (bid, ask) in enumerate(zip(data['bids'][:5], data['asks'][:5])):
self._save_to_database(
timestamp=timestamp,
datetime_str=dt,
bid_price=float(bid[0]),
bid_quantity=float(bid[1]),
ask_price=float(ask[0]),
ask_quantity=float(ask[1]),
update_id=timestamp
)
except Exception as e:
print(f"❌ Error processing message: {e}")
def _save_to_database(self, timestamp, datetime_str, bid_price,
bid_quantity, ask_price, ask_quantity, update_id):
"""บันทึกข้อมูลลง SQLite"""
try:
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
cursor.execute('''
INSERT INTO orderbook_snapshots
(timestamp, datetime, bid_price, bid_quantity,
ask_price, ask_quantity, update_id)
VALUES (?, ?, ?, ?, ?, ?, ?)
''', (timestamp, datetime_str, bid_price, bid_quantity,
ask_price, ask_quantity, update_id))
conn.commit()
conn.close()
except Exception as e:
print(f"❌ Database error: {e}")
def on_error(self, ws, error):
"""จัดการเมื่อเกิดข้อผิดพลาด"""
print(f"❌ WebSocket Error: {error}")
def on_close(self, ws, close_status_code, close_msg):
"""จัดการเมื่อ WebSocket ถูกปิด"""
print(f"🔴 WebSocket closed: {close_status_code} - {close_msg}")
def on_open(self, ws):
"""จัดการเมื่อ WebSocket เปิด"""
print(f"🟢 WebSocket connected to {self.stream_url}")
self.is_running = True
def start_recording(self, duration_seconds=None):
"""
เริ่มบันทึกข้อมูล Orderbook
duration_seconds: ระยะเวลาการบันทึก (ถ้าไม่ระบุจะบันทึกต่อเนื่อง)
"""
ws = websocket.WebSocketApp(
self.stream_url,
on_message=self.on_message,
on_error=self.on_error,
on_close=self.on_close,
on_open=self.on_open
)
ws_thread = threading.Thread(target=ws.run_forever)
ws_thread.daemon = True
ws_thread.start()
print(f"📍 เริ่มบันทึกข้อมูล Orderbook สำหรับ {self.symbol}")
try:
if duration_seconds:
time.sleep(duration_seconds)
ws.close()
else:
# บันทึกจนกว่าจะกด Ctrl+C
while self.is_running:
time.sleep(1)
except KeyboardInterrupt:
print("\n⏹️ หยุดบันทึกข้อมูล")
ws.close()
def load_data_to_dataframe(self, start_time=None, end_time=None):
"""โหลดข้อมูลจาก database เป็น DataFrame"""
conn = sqlite3.connect(self.db_path)
query = "SELECT * FROM orderbook_snapshots WHERE 1=1"
params = []
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 if params else None)
conn.close()
return df
def get_orderbook_statistics(self):
"""สร้างสถิติจากข้อมูลที่บันทึกไว้"""
conn = sqlite3.connect(self.db_path)
stats_query = '''
SELECT
COUNT(*) as total_records,
MIN(datetime) as start_time,
MAX(datetime) as end_time,
AVG(bid_quantity) as avg_bid_qty,
AVG(ask_quantity) as avg_ask_qty,
AVG(ask_price - bid_price) as avg_spread
FROM orderbook_snapshots
'''
stats = pd.read_sql_query(stats_query, conn).iloc[0]
conn.close()
return stats
ตัวอย่างการใช้งาน
recorder = BinanceOrderbookRecorder(symbol='btcusdt', db_path='btc_orderbook.db')
เริ่มบันทึกข้อมูล 60 วินาที
print("🔄 เริ่มบันทึกข้อมูล Orderbook สำหรับ 60 วินาที...")
recorder.start_recording(duration_seconds=60)
โหลดข้อมูลและแสดงสถิติ
stats = recorder.get_orderbook_statistics()
print(f"\n📊 สถิติข้อมูลที่บันทึก:")
print(f" จำนวน records: {stats['total_records']:,}")
print(f" ช่วงเวลา: {stats['start_time']} ถึง {stats['end_time']}")
print(f" Spread เฉลี่ย: ${stats['avg_spread']:.2f}")
วิธีที่ 3: ใช้บริการ Third-Party Data Provider
สำหรับนักพัฒนาที่ต้องการข้อมูล Orderbook ย้อนหลังนานกว่า 7 วัน การใช้บริการจาก Data Provider เฉพาะทางเป็นทางเลือกที่ดีกว่า เนื่องจาก Binance ไม่ได้เก็บข้อมูลราคาละเอียดระดับ Orderbook ไว้ให้นานเกินไป
เหมาะกับใคร / ไม่เหมาะกับใคร
| กลุ่มผู้ใช้งาน | เหมาะกับ | ไม่เหมาะกับ |
|---|---|---|
| นักเทรดรายวัน (Day Trader) | ใช้ Binance API โดยตรง เพราะต้องการข้อมูลปัจจุบันและไม่ต้องการข้อมูลย้อนหลังนาน | ผู้ที่ต้องการข้อมูลย้อนหลังหลายเดือน |
| Quantitative Researcher | ใช้ WebSocket + Local Database หรือ Third-party Provider เพื่อเก็บข้อมูลละเอียด | ผู้ที่มีงบประมาณจำกัดและต้องการแค่ข้อมูล OHLCV |
| สถาบันหรือกองทุน | ใช้บริการ premium เช่น CoinAPI, Kaiko ที่ให้ข้อมูลระดับ Tick-by-Tick พร้อมคุณภาพ guaranteed | ผู้ที่เริ่มต้นศึกษาหรือทำโปรเจกต์ขนาดเล็ก |
| นักพัฒนา AI/ML | ใช้ HolySheep AI สำหรับวิเคราะห์ข้อมูลและสร้างโมเดล prediction ด้วย LLM ราคาถูก | ผู้ที่ต้องการข้อมูล Real-time สดๆ ตลอดเวลา |
ราคาและ ROI
สำหรับการทำ Quantitative Research การเลือกเครื่องมือที่เหมาะสมจะช่วยประหยัดต้นทุนได้มาก โดยเฉพาะค่า API และค่า Compute สำหรับการประมวลผลข้อมูลขนาดใหญ่
| บริการ | ราคา/เดือน (USD) | ปริมาณข้อมูล | ความคุ้มค่า |
|---|---|---|---|
| Binance API (Free) | $0 | 500 requests/นาที | ⭐⭐⭐ ดีสำหรับเริ่มต้น |
| CoinAPI | $79 - $399 | 10K - 100K requests/วัน | ⭐⭐ เหมาะกับองค์กร |
| Kaiko | $500+ | Unlimited historical | ⭐⭐ แพงแต่ครบ |
| HolySheep AI | เริ่มต้นฟรี | API + Compute | ⭐⭐⭐⭐⭐ ประหยัด 85%+ |
ทำไมต้องเลือก HolySheep
สำหรับนักพัฒนา Quantitative Trading ที่ต้องการใช้ LLM ในการวิเคราะห์ข้อมูล Orderbook และสร้างกลยุทธ์ สมัครที่นี่ HolySheep AI เป็นตัวเลือกที่คุ้มค่าที่สุด:
- อัตราแลกเปลี่ยนพิเศษ: ¥1 = $1 (ประหยัดมากกว่า 85% เมื่อเทียบกับ OpenAI หรือ Anthropic)
- รองรับหลายโมเดล: GPT-4.1, Claude Sonnet 4.5, Gemini 2.5 Flash, DeepSeek V3.2
- ความเร็ว: Latency ต่ำกว่า 50ms สำหรับการประมวลผล
- ชำระเงินง่าย: รองรับ WeChat และ Alipay
- เครดิตฟรี: เมื่อลงทะเบียนใหม่จะได้รับเครดิตฟรีสำหรับทดลองใช้งาน
ราคา LLM 2026 (ราคาต่อล้าน Token):
| โมเดล | ราคาปกติ (USD) | ราคา HolySheep (USD) | ประหยัด |
|---|---|---|---|
| GPT-4.1 | $15-60 | $8 | ~47-87% |
| Claude Sonnet 4.5 | $30 | $15 | 50% |
| Gemini 2.5 Flash | $10 | $2.50 | 75% |
| DeepSeek V3.2 | $2.80 | $0.42 | 85% |
ข้อผิดพลาดที่พบบ่อยและวิธีแก้ไข
1. Rate Limit Error 429
ปัญหา: เมื่อส่ง request ไปยัง Binance API เกินจำนวนที่กำหนด จะได้รับ error 429
import time
from binance.error import ClientError
def safe_api_call(func, max_retries=3, backoff_factor=2):
"""
ฟังก์ชันสำหรับเรียก API อย่างปลอดภัยพร้อม retry logic
"""
for attempt in range(max_retries):
try:
result = func()
return result
except ClientError as e:
if e.error_code == -1003: # Rate limit error
wait_time = backoff_factor ** attempt
print(f"⚠️ Rate limit hit, waiting {wait_time} seconds...")
time.sleep(wait_time)
else:
print(f"❌ API Error: {e.error_message}")
return None
except Exception as e:
print(f"❌ Unexpected error: {e}")
return None
print(f"❌ Max retries ({max_retries}) exceeded")
return None
ตัวอย่างการใช้งาน
fetcher = BinanceOrderbookFetcher()
result = safe_api_call(lambda: fetcher.get_orderbook_snapshot())
2. WebSocket Disconnection บ่อยครั้ง
ปัญหา: WebSocket หลุดการเชื่อมต่อบ่อยทำให้ข้อมูลขาดหาย
import websocket
import threading
import time
class RobustWebSocketConnection:
"""WebSocket ที่มีระบบ reconnect อัตโนมัติ"""
def __init__(self, url, reconnect_delay=5, max_reconnects=10):
self.url = url
self.reconnect_delay = reconnect_delay
self.max_reconnects = max_reconnects
self.ws = None
self.reconnect_count = 0
self.is_running = False
def start(self):
"""เริ่มเชื่อมต่อพร้อม auto-reconnect"""
self.is_running = True
while self.is_running and self.reconnect_count < self.max_reconnects:
try:
print(f"🔄 พยายามเชื่อมต่อครั้งที่ {self.reconnect_count + 1}")
self.ws = websocket.WebSocketApp(
self.url,
on_message=self.on_message,
on_error=self.on_error,
on_close=self.on_close,
on_open=self.on_open
)
# ใช้ run_forever พร้อม ping_interval เพื่อรักษาการเชื่อมต่อ
self.ws.run_forever(ping_interval=30, ping_timeout=10)
except Exception as e:
print(f"❌ Connection error: {e}")
self.reconnect_count += 1
if self.reconnect_count < self.max_reconnects:
print(f"⏰ รอ {self.reconnect_delay} วินาทีก่อนเชื่อมต่อใหม่...")
time.sleep(self.reconnect_delay)
# เพิ่ม delay ทุกครั้งที่ reconnect
self.reconnect_delay = min(self.reconnect_delay * 1.5, 60)
def on