ในโลกของการเทรดคริปโตที่มีการแข่งขันสูง การทำ Backtesting ด้วยข้อมูลประวัติศาสตร์เป็นกุญแจสำคัญที่ทำให้นักเทรดพัฒนากลยุทธ์ที่ทำกำไรได้จริง บทความนี้จะพาคุณเรียนรู้วิธีดึงข้อมูล Bybit Perpetual Futures มาประมวลผลด้วย Python Pandas และนำไปทำ Backtesting เพื่อทดสอบประสิทธิภาพของกลยุทธ์การเทรดของคุณ
กรณีศึกษา: ทีมสตาร์ทอัพ AI ในกรุงเทพฯ
บริบทธุรกิจ
ทีมสตาร์ทอัพ AI แห่งหนึ่งในกรุงเทพฯ กำลังพัฒนาระบบเทรดอัตโนมัติสำหรับ Bybit Perpetual Futures โดยใช้ Machine Learning เพื่อวิเคราะห์แนวโน้มราคาและสร้างสัญญาณซื้อขาย ทีมมีกลยุทธ์การเทรดที่พัฒนาขึ้นแล้ว 8 กลยุทธ์ และต้องการทำ Backtesting กับข้อมูลย้อนหลัง 2 ปี เพื่อทดสอบว่ากลยุทธ์ใดให้ผลตอบแทนดีที่สุด
จุดเจ็บปวด
ทีมเผชิญปัญหาสำคัญหลายประการ:
- ค่าใช้จ่ายสูง: การเรียก API สำหรับดึงข้อมูลประวัติศาสตร์มีค่าใช้จ่ายมากกว่า $420 ต่อเดือน
- ความเร็วต่ำ: การประมวลผลข้อมูลขนาดใหญ่ใช้เวลานานเนื่องจาก API Response Time สูงถึง 420ms
- ปัญหาการจัดการข้อมูล: ข้อมูลจาก Bybit มีรูปแบบที่ไม่ตรงกับความต้องการ ต้องทำ Data Cleaning และ Transformation หลายขั้นตอน
- ความซับซ้อนของ Timezone: ข้อมูลจาก Bybit ใช้ UTC แต่ทีมต้องการแสดงผลเป็นเวลาท้องถิ่นเอเชีย/กรุงเทพฯ
การย้ายมาใช้ HolySheep AI
หลังจากทดลองใช้หลายบริการ ทีมตัดสินใจย้ายมาใช้ HolySheep AI เนื่องจากเหตุผลหลักคือ อัตรา ¥1=$1 ซึ่งประหยัดได้มากกว่า 85% เมื่อเทียบกับบริการอื่น และรองรับการชำระเงินผ่าน WeChat/Alipay ทำให้การชำระเงินสะดวกสำหรับทีมในเอเชีย
ขั้นตอนการย้ายระบบ
1. การเปลี่ยน Base URL
ทีมเปลี่ยนจาก Base URL เดิมมาใช้ https://api.holysheep.ai/v1 ซึ่งให้ Performance ที่เหนือกว่าพร้อม Latency ต่ำกว่า 50ms
# โค้ดเดิม (ก่อนย้าย)
BASE_URL = "https://api.bybit.com/v5"
โค้ดใหม่ (หลังย้ายมาใช้ HolySheep)
BASE_URL = "https://api.holysheep.ai/v1"
API_KEY = "YOUR_HOLYSHEEP_API_KEY"
2. การหมุนคีย์และการจัดการ API Keys
ทีมตั้งค่า API Key Rotation อัตโนมัติเพื่อป้องกันปัญหา Rate Limiting และเพิ่มความปลอดภัย
import os
from datetime import datetime, timedelta
class HolySheepAPI:
def __init__(self, api_keys: list):
self.api_keys = api_keys
self.current_key_index = 0
self.key_usage_count = {key: 0 for key in api_keys}
self.reset_date = datetime.now() + timedelta(days=1)
def get_current_key(self):
# หมุนคีย์ทุก 1000 คำขอเพื่อหลีกเลี่ยง Rate Limit
if self.key_usage_count[self.current_key()] >= 1000:
self.rotate_key()
return self.current_key()
def current_key(self):
return self.api_keys[self.current_key_index]
def rotate_key(self):
self.current_key_index = (self.current_key_index + 1) % len(self.api_keys)
print(f"หมุนคีย์ไปยัง: {self.current_key()[:8]}... ที่ {datetime.now()}")
def call_api(self, endpoint: str, params: dict = None):
import requests
headers = {"Authorization": f"Bearer {self.get_current_key()}"}
response = requests.get(
f"https://api.holysheep.ai/v1/{endpoint}",
headers=headers,
params=params
)
self.key_usage_count[self.current_key()] += 1
return response.json()
ตัวอย่างการใช้งาน
api_keys = [
"YOUR_HOLYSHEEP_API_KEY_1",
"YOUR_HOLYSHEEP_API_KEY_2",
"YOUR_HOLYSHEEP_API_KEY_3"
]
api = HolySheepAPI(api_keys)
3. Canary Deployment
ทีมใช้กลยุทธ์ Canary Deployment โดยเริ่มจากการย้าย 10% ของ Request ไปใช้ HolySheep ก่อน จากนั้นค่อยๆ เพิ่มสัดส่วนจนถึง 100%
import random
class CanaryDeploy:
def __init__(self, canary_percentage: float = 0.1):
self.canary_percentage = canary_percentage
self.stats = {"holysheep": 0, "bybit": 0}
def route_request(self) -> str:
if random.random() < self.canary_percentage:
self.stats["holysheep"] += 1
return "holysheep"
else:
self.stats["bybit"] += 1
return "bybit"
def get_stats(self) -> dict:
total = sum(self.stats.values())
return {
"holysheep_pct": self.stats["holysheep"] / total * 100,
"bybit_pct": self.stats["bybit"] / total * 100,
"total_requests": total
}
def increase_canary(self, increment: float = 0.1):
self.canary_percentage = min(1.0, self.canary_percentage + increment)
print(f"เพิ่ม Canary Traffic เป็น {self.canary_percentage * 100}%")
เริ่มต้นด้วย 10% ไป HolySheep
deployer = CanaryDeploy(canary_percentage=0.1)
print(deployer.get_stats()) # {'holysheep_pct': 10.0, 'bybit_pct': 90.0, ...}
ตัวชี้วัด 30 วันหลังจากย้าย
| ตัวชี้วัด | ก่อนย้าย (Bybit Direct) | หลังย้าย (HolySheep) | การปรับปรุง |
|---|---|---|---|
| API Response Time | 420ms | 180ms | ↓ 57% (เร็วขึ้น 240ms) |
| ค่าใช้จ่ายรายเดือน | $4,200 | $680 | ↓ 84% (ประหยัด $3,520) |
| เวลาประมวลผล Backtesting | 8.5 ชั่วโมง | 3.2 ชั่วโมง | ↓ 62% (เร็วขึ้น 5.3 ชม.) |
| ความสำเร็จของ Request | 96.2% | 99.8% | ↑ 3.6% |
การดึงข้อมูล Bybit Perpetual Futures ด้วย Python
การติดตั้งและตั้งค่าเบื้องต้น
# ติดตั้งไลบรารีที่จำเป็น
pip install pandas numpy requests python-binance pandas-ta
นำเข้าไลบรารี
import pandas as pd
import numpy as np
import requests
from datetime import datetime, timedelta
import pytz
ตั้งค่า Timezone สำหรับประเทศไทย
THB_TZ = pytz.timezone('Asia/Bangkok')
ตั้งค่า HolySheep API
BASE_URL = "https://api.holysheep.ai/v1"
API_KEY = "YOUR_HOLYSHEEP_API_KEY"
def fetch_bybit_data(symbol: str, interval: str, start_time: int, end_time: int):
"""
ดึงข้อมูล OHLCV จาก Bybit ผ่าน HolySheep API
สำหรับ Backtesting
Parameters:
- symbol: คู่เทรด เช่น BTCUSDT
- interval: ช่วงเวลา เช่น 1m, 5m, 15m, 1h, 4h, 1d
- start_time: timestamp เริ่มต้น (มิลลิวินาที)
- end_time: timestamp สิ้นสุด (มิลลิวินาที)
"""
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
}
params = {
"category": "perpetual",
"symbol": symbol,
"interval": interval,
"start": start_time,
"end": end_time,
"limit": 1000 # สูงสุด 1000 records ต่อครั้ง
}
try:
response = requests.get(
f"{BASE_URL}/market/kline",
headers=headers,
params=params,
timeout=30
)
if response.status_code == 200:
data = response.json()
if data.get("retCode") == 0:
return data.get("result", {}).get("list", [])
else:
print(f"เกิดข้อผิดพลาด: {data.get('retMsg')}")
return None
else:
print(f"HTTP Error: {response.status_code}")
return None
except requests.exceptions.RequestException as e:
print(f"Request Exception: {e}")
return None
print("เตรียมพร้อมสำหรับการดึงข้อมูล Bybit Perpetual Futures")
การประมวลผลข้อมูลด้วย Pandas
def fetch_and_process_bybit_data(symbol: str, interval: str, days: int = 30):
"""
ดึงและประมวลผลข้อมูล Bybit สำหรับ Backtesting
Parameters:
- symbol: คู่เทรด เช่น BTCUSDT, ETHUSDT
- interval: ช่วงเวลา (1m, 5m, 15m, 1h, 4h, 1d)
- days: จำนวนวันย้อนหลัง
"""
# คำนวณ timestamp
end_time = int(datetime.now().timestamp() * 1000)
start_time = int((datetime.now() - timedelta(days=days)).timestamp() * 1000)
# ดึงข้อมูล
raw_data = fetch_bybit_data(symbol, interval, start_time, end_time)
if not raw_data:
return None
# สร้าง DataFrame
df = pd.DataFrame(raw_data, columns=[
'timestamp', 'open', 'high', 'low', 'close', 'volume', 'turnover'
])
# แปลงประเภทข้อมูล
numeric_cols = ['open', 'high', 'low', 'close', 'volume', 'turnover']
for col in numeric_cols:
df[col] = pd.to_numeric(df[col], errors='coerce')
# แปลง timestamp เป็น datetime
df['datetime'] = pd.to_datetime(df['timestamp'].astype(int), unit='ms')
# แปลงเป็น timezone ประเทศไทย
df['datetime_thb'] = df['datetime'].dt.tz_localize('UTC').dt.tz_convert('Asia/Bangkok')
# เรียงข้อมูลจากเก่าไปใหม่
df = df.sort_values('datetime').reset_index(drop=True)
# เพิ่มคอลัมน์ที่มีประโยชน์สำหรับ Backtesting
df['returns'] = df['close'].pct_change() # อัตราผลตอบแทนราย period
df['log_returns'] = np.log(df['close'] / df['close'].shift(1)) # Log returns
# คำนวณ Moving Averages
df['sma_20'] = df['close'].rolling(window=20).mean()
df['sma_50'] = df['close'].rolling(window=50).mean()
df['ema_12'] = df['close'].ewm(span=12, adjust=False).mean()
df['ema_26'] = df['close'].ewm(span=26, adjust=False).mean()
# คำนวณ RSI
delta = df['close'].diff()
gain = (delta.where(delta > 0, 0)).rolling(window=14).mean()
loss = (-delta.where(delta < 0, 0)).rolling(window=14).mean()
rs = gain / loss
df['rsi'] = 100 - (100 / (1 + rs))
# คำนวณ Bollinger Bands
df['bb_middle'] = df['close'].rolling(window=20).mean()
df['bb_std'] = df['close'].rolling(window=20).std()
df['bb_upper'] = df['bb_middle'] + (df['bb_std'] * 2)
df['bb_lower'] = df['bb_middle'] - (df['bb_std'] * 2)
# คำนวณ Volume Profile
df['volume_sma_20'] = df['volume'].rolling(window=20).mean()
return df
ตัวอย่างการดึงข้อมูล BTCUSDT 90 วันย้อนหลัง
df_btc = fetch_and_process_bybit_data("BTCUSDT", "1h", days=90)
print(f"ดึงข้อมูลสำเร็จ: {len(df_btc)} records")
print(df_btc.head())
การสร้างระบบ Backtesting
def backtest_strategy(df: pd.DataFrame, initial_capital: float = 10000,
strategy_type: str = "sma_crossover"):
"""
ระบบ Backtesting สำหรับทดสอบกลยุทธ์
Parameters:
- df: DataFrame ที่มีข้อมูล OHLCV และ indicators
- initial_capital: เงินทุนเริ่มต้น (USD)
- strategy_type: ประเภทกลยุทธ์ ("sma_crossover", "rsi", "bollinger")
"""
df = df.copy()
df['position'] = 0 # 0 = ถือเงินสด, 1 = ถือสินทรัพย์
df['trade_signal'] = 0 # 1 = ซื้อ, -1 = ขาย, 0 = ถือ
if strategy_type == "sma_crossover":
# กลยุทธ์ SMA Crossover
df['trade_signal'] = np.where(
df['sma_20'] > df['sma_50'], 1, -1
)
# ส่งสัญญาณซื้อ/ขายเมื่อเกิดการตัดกัน
df['trade_signal'] = df['trade_signal'].diff().fillna(0)
df.loc[df['trade_signal'] == 2, 'trade_signal'] = 1 # ซื้อ
df.loc[df['trade_signal'] == -2, 'trade_signal'] = -1 # ขาย
elif strategy_type == "rsi":
# กลยุทธ์ RSI Oversold/Overbought
df.loc[df['rsi'] < 30, 'trade_signal'] = 1 # ซื้อเมื่อ RSI < 30
df.loc[df['rsi'] > 70, 'trade_signal'] = -1 # ขายเมื่อ RSI > 70
elif strategy_type == "bollinger":
# กลยุทธ์ Bollinger Bands
df.loc[df['close'] < df['bb_lower'], 'trade_signal'] = 1 # ซื้อเมื่อราคาต่ำกว่า Lower Band
df.loc[df['close'] > df['bb_upper'], 'trade_signal'] = -1 # ขายเมื่อราคาสูงกว่า Upper Band
# จำลองการเทรด
capital = initial_capital
position = 0 # จำนวนสินทรัพย์ที่ถือ
position_size = 0.95 # ใช้ 95% ของทุนต่อครั้ง
trades = []
portfolio_values = []
for i, row in df.iterrows():
signal = row['trade_signal']
price = row['close']
if signal == 1 and position == 0: # สัญญาณซื้อ
buy_amount = capital * position_size
position = buy_amount / price
capital -= buy_amount
trades.append({
'datetime': row['datetime_thb'],
'type': 'BUY',
'price': price,
'amount': position,
'capital': capital
})
elif signal == -1 and position > 0: # สัญญาณขาย
sell_value = position * price
capital += sell_value
trades.append({
'datetime': row['datetime_thb'],
'type': 'SELL',
'price': price,
'amount': position,
'capital': capital
})
position = 0
# คำนวณมูลค่าพอร์ตรวม
total_value = capital + (position * price)
portfolio_values.append(total_value)
df['portfolio_value'] = portfolio_values
# คำนวณผลตอบแทน
total_return = (portfolio_values[-1] - initial_capital) / initial_capital * 100
# คำนวณ Max Drawdown
df['cummax'] = df['portfolio_value'].cummax()
df['drawdown'] = (df['portfolio_value'] - df['cummax']) / df['cummax'] * 100
max_drawdown = df['drawdown'].min()
# คำนวณ Sharpe Ratio
daily_returns = df['portfolio_value'].pct_change().dropna()
sharpe_ratio = (daily_returns.mean() / daily_returns.std()) * np.sqrt(252) if daily_returns.std() > 0 else 0
return {
'total_return': total_return,
'max_drawdown': max_drawdown,
'sharpe_ratio': sharpe_ratio,
'total_trades': len(trades),
'final_capital': portfolio_values[-1],
'trades': pd.DataFrame(trades),
'df': df
}
ทดสอบกลยุทธ์
result = backtest_strategy(df_btc, initial_capital=10000, strategy_type="sma_crossover")
print(f"ผลตอบแทนรวม: {result['total_return']:.2f}%")
print(f"Max Drawdown: {result['max_drawdown']:.2f}%")
print(f"Sharpe Ratio: {result['sharpe_ratio']:.2f}")
print(f"จำนวนการเทรด: {result['total_trades']}")
การเปรียบเทียบกลยุทธ์หลายแบบ
def compare_strategies(df: pd.DataFrame, initial_capital: float = 10000):
"""
เปรียบเทียบผลตอบแทนของกลยุทธ์หลายแบบ
"""
strategies = ['sma_crossover', 'rsi', 'bollinger']
results = []
for strategy in strategies:
result = backtest_strategy(df, initial_capital, strategy)
results.append({
'Strategy': strategy,
'Total Return (%)': round(result['total_return'], 2),
'Max Drawdown (%)': round(result['max_drawdown'], 2),
'Sharpe Ratio': round(result['sharpe_ratio'], 2),
'Total Trades': result['total_trades'],
'Final Capital ($)': round(result['final_capital'], 2)
})
return pd.DataFrame(results)
เปรียบเทียบกลยุทธ์
comparison = compare_strategies(df_btc, initial_capital=10000)
print(comparison.to_string(index=False))
หากลยุทธ์ที่ดีที่สุด
best_strategy = comparison.loc[comparison['Total Return (%)'].idxmax()]
print(f"\nกลยุทธ์ที่ให้ผลตอบแทนดีที่สุด: {best_strategy['Strategy']}")
print(f"ผลตอบแทน: {best_strategy['Total Return (%)']}%")