การดึงข้อมูล K线 (Candlestick) จาก Binance API เป็นพื้นฐานสำคัญสำหรับนักพัฒนา trading bot และนักวิเคราะห์ทางเทคนิค แต่ในทางปฏิบัติ หลายคนเจอปัญหาเช่น ConnectionError: timeout ขณะดึงข้อมูลราคาย้อนหลัง หรือ 401 Unauthorized เมื่อใช้ API key ผิดวิธี บทความนี้จะสอนวิธีดึงข้อมูล K线 Binance แต่ละ timeframe (1m, 5m, 1h, 1d) อย่างมีประสิทธิภาพ พร้อมกลยุทธ์จัดเก็บที่ถูกต้อง และวิธีแก้ปัญหาที่พบบ่อย
ทำไมต้องเก็บข้อมูล K线 Binance เอง?
Binance มี API ให้ใช้ฟรี แต่มีข้อจำกัด:
- Rate Limit: 1200 request/นาที สำหรับ weighted average price
- Historical Data: ดึงได้แค่ 1000 candles ต่อครั้ง
- เสถียรภาพ: เมื่อ server Binance ล่ม ข้อมูลหายทันที
- ความเร็ว: ต้องรอ API response ทุกครั้ง ทำให้ backtest ช้า
การเก็บข้อมูลไว้เองช่วยให้:
- เข้าถึงข้อมูลได้ทันทีไม่ต้องรอ API
- ประหยัด rate limit สำหรับ order และ market data อื่นๆ
- ทำ backtest ได้รวดเร็วหลายพันครั้ง
- มีข้อมูล backup กรณี API มีปัญหา
การดึงข้อมูล K线 Binance ด้วย Python
1. ติดตั้ง Library ที่จำเป็น
pip install pandas requests python-binance schedule sqlalchemy
2. สคริปต์ดึงข้อมูล K线 ทุก Timeframe
import requests
import pandas as pd
from datetime import datetime, timedelta
import time
การดึงข้อมูล K线 จาก Binance Public API
BASE_URL = "https://api.binance.com"
def get_klines(symbol, interval, start_time=None, limit=1000):
"""
ดึงข้อมูล K线 จาก Binance
Parameters:
- symbol: เช่น 'BTCUSDT', 'ETHUSDT'
- interval: '1m', '5m', '15m', '1h', '4h', '1d', '1w'
- start_time: timestamp เริ่มต้น (milliseconds)
- limit: จำนวน candles สูงสุด 1000 ต่อครั้ง
Returns:
- DataFrame พร้อมข้อมูล OHLCV
"""
endpoint = "/api/v3/klines"
params = {
"symbol": symbol.upper(),
"interval": interval,
"limit": limit
}
if start_time:
params["startTime"] = start_time
try:
response = requests.get(f"{BASE_URL}{endpoint}", params=params, timeout=10)
response.raise_for_status()
data = response.json()
# แปลงเป็น DataFrame
df = pd.DataFrame(data, columns=[
"open_time", "open", "high", "low", "close", "volume",
"close_time", "quote_volume", "trades", "taker_buy_base",
"taker_buy_quote", "ignore"
])
# แปลงประเภทข้อมูล
numeric_cols = ["open", "high", "low", "close", "volume", "quote_volume"]
for col in numeric_cols:
df[col] = pd.to_numeric(df[col], errors='coerce')
# แปลง timestamp
df["open_time"] = pd.to_datetime(df["open_time"], unit="ms")
df["close_time"] = pd.to_datetime(df["close_time"], unit="ms")
return df
except requests.exceptions.Timeout:
print(f"⏰ Timeout error: {symbol} {interval}")
return None
except requests.exceptions.HTTPError as e:
print(f"❌ HTTP Error: {e}")
return None
except Exception as e:
print(f"❌ Error: {e}")
return None
ทดสอบการดึงข้อมูล
if __name__ == "__main__":
# ดึงข้อมูล BTCUSDT รายชั่วโมง 1000 แท่งล่าสุด
df = get_klines("BTCUSDT", "1h", limit=1000)
if df is not None:
print(f"✅ ดึงข้อมูลสำเร็จ: {len(df)} candles")
print(df.tail())
3. ระบบดึงข้อมูลย้อนหลังแบบต่อเนื่อง
import sqlite3
from datetime import datetime, timedelta
import time
import schedule
การเชื่อมต่อ SQLite สำหรับจัดเก็บข้อมูล
DB_PATH = "binance_klines.db"
def init_database():
"""สร้างตารางถ้ายังไม่มี"""
conn = sqlite3.connect(DB_PATH)
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS klines (
id INTEGER PRIMARY KEY AUTOINCREMENT,
symbol TEXT NOT NULL,
interval TEXT NOT NULL,
open_time TEXT NOT NULL,
open REAL,
high REAL,
low REAL,
close REAL,
volume REAL,
quote_volume REAL,
trades INTEGER,
UNIQUE(symbol, interval, open_time)
)
""")
conn.commit()
conn.close()
def save_to_database(df, symbol, interval):
"""บันทึกข้อมูลลงฐานข้อมูล"""
if df is None or df.empty:
return False
conn = sqlite3.connect(DB_PATH)
for _, row in df.iterrows():
try:
cursor = conn.cursor()
cursor.execute("""
INSERT OR REPLACE INTO klines
(symbol, interval, open_time, open, high, low, close, volume, quote_volume, trades)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""", (
symbol.upper(),
interval,
row["open_time"].isoformat(),
row["open"],
row["high"],
row["low"],
row["close"],
row["volume"],
row["quote_volume"],
int(row["trades"])
))
except Exception as e:
print(f"⚠️ Insert error: {e}")
conn.commit()
conn.close()
return True
def get_latest_timestamp(symbol, interval):
"""ดึง timestamp ล่าสุดที่มีในฐานข้อมูล"""
conn = sqlite3.connect(DB_PATH)
cursor = conn.cursor()
cursor.execute("""
SELECT open_time FROM klines
WHERE symbol = ? AND interval = ?
ORDER BY open_time DESC LIMIT 1
""", (symbol.upper(), interval))
result = cursor.fetchone()
conn.close()
if result:
return int(datetime.fromisoformat(result[0]).timestamp() * 1000)
return None
def fetch_all_historical(symbol, interval, months=12):
"""
ดึงข้อมูลย้อนหลังหลายเดือน
Binance limit: 1000 candles ต่อ request
"""
init_database()
# หาเวลาเริ่มต้น
latest_ts = get_latest_timestamp(symbol, interval)
if latest_ts is None:
# ถ้าไม่มีข้อมูล ใช้เวลา 12 เดือนก่อน
start_time = int((datetime.now() - timedelta(days=months * 30)).timestamp() * 1000)
else:
start_time = latest_ts
all_data = []
current_start = start_time
total_fetched = 0
print(f"📥 เริ่มดึงข้อมูล {symbol} {interval} ตั้งแต่ {datetime.fromtimestamp(current_start/1000)}")
while True:
df = get_klines(symbol, interval, start_time=current_start, limit=1000)
if df is None or df.empty:
break
all_data.append(df)
total_fetched += len(df)
# ใช้เวลา close ของแท่งสุดท้ายเป็น start ครั้งต่อไป
last_close_time = int(df["close_time"].iloc[-1].timestamp() * 1000) + 1
current_start = last_close_time
print(f" ✅ ดึงได้ {len(df)} candles, รวม: {total_fetched}")
# รอเพื่อไม่ให้ rate limit
time.sleep(0.3)
# ถ้าได้น้อยกว่า 1000 แปลว่าถึงข้อมูลล่าสุดแล้ว
if len(df) < 1000:
break
# ป้องกัน infinite loop
if total_fetched > 50000:
print("⚠️ ถึงขีดจำกัดการดึงข้อมูล")
break
# รวมข้อมูลทั้งหมดและบันทึก
if all_data:
combined_df = pd.concat(all_data, ignore_index=True)
combined_df = combined_df.drop_duplicates(subset=["open_time"])
combined_df = combined_df.sort_values("open_time")
save_to_database(combined_df, symbol, interval)
print(f"✅ บันทึกสำเร็จ: {len(combined_df)} candles")
return combined_df
return None
ทดสอบการดึงข้อมูล 12 เดือน
if __name__ == "__main__":
symbols = ["BTCUSDT", "ETHUSDT"]
intervals = ["1h", "4h", "1d"]
for symbol in symbols:
for interval in intervals:
fetch_all_historical(symbol, interval, months=12)
time.sleep(2) # รอระหว่างแต่ละคู่
เหมาะกับใคร / ไม่เหมาะกับใคร
| กลุ่มผู้ใช้ | เหมาะกับ | ไม่เหมาะกับ |
|---|---|---|
| นักพัฒนา Trading Bot | ต้องการ backtest หลายพันครั้ง, ดึงข้อมูลเร็วไม่รอ API | ผู้เริ่มต้นที่มีข้อมูลไม่มาก |
| นักวิเคราะห์ทางเทคนิค | วิเคราะห์กราฟหลาย timeframe, เปรียบเทียบ indicator | ผู้ที่ใช้แค่ chart บนเว็บ Binance |
| Data Scientist | สร้างโมเดล ML ทำนายราคา, ต้องการ dataset ขนาดใหญ่ | ผู้ที่ต้องการข้อมูลแค่ real-time |
| สถาบันการเงิน | ต้องการข้อมูล backup, compliance, audit trail | ผู้ใช้รายบุคคลทั่วไป |
ราคาและ ROI
การเก็บข้อมูล K线 ด้วยวิธีข้างต้นใช้ได้กับทุก API แต่ถ้าต้องการประมวลผลข้อมูลด้วย AI/ML แนะนำใช้ HolySheep AI ซึ่งมีข้อดีด้านค่าใช้จ่าย:
| ผู้ให้บริการ | ราคา GPT-4.1 | ราคา Claude 4.5 | ราคา DeepSeek V3.2 | ความเร็ว |
|---|---|---|---|---|
| HolySheep AI | $8/MTok | $15/MTok | $0.42/MTok | <50ms |
| OpenAI โดยตรง | $15/MTok | ไม่มี | ไม่มี | 200-500ms |
| Anthropic โดยตรง | ไม่มี | $18/MTok | ไม่มี | 300-600ms |
ROI จากการใช้ HolySheep:
- ประหยัด 85%+ เมื่อเทียบกับ OpenAI โดยตรง สำหรับ DeepSeek V3.2
- เวลาตอบสนอง <50ms เร็วกว่า API โดยตรง 5-10 เท่า
- รองรับ WeChat/Alipay สำหรับผู้ใช้ในประเทศจีน
- อัตราแลกเปลี่ยน ¥1=$1 คุ้มค่าสำหรับผู้ใช้ทั่วโลก
ทำไมต้องเลือก HolySheep
สำหรับนักพัฒนาที่ต้องการประมวลผลข้อมูล K线 ด้วย AI เพื่อ:
- วิเคราะห์ Pattern: ใช้ LLM ตรวจหา candlestick patterns อัตโนมัติ
- สร้างรายงาน: สรุปแนวโน้มตลาดด้วย AI จากข้อมูลหลาย timeframe
- Predictive Model: ใช้ AI ช่วยวิเคราะห์ความสัมพันธ์ของ indicators
- Backtest Optimization: ให้ AI ช่วยหา parameters ที่เหมาะสม
API ของ HolySheep AI รองรับทุกโมเดลยอดนิยม (GPT-4.1, Claude Sonnet 4.5, Gemini 2.5 Flash, DeepSeek V3.2) ด้วยราคาที่ถูกที่สุดในตลาด
ข้อผิดพลาดที่พบบ่อยและวิธีแก้ไข
1. ConnectionError: timeout - ดึงข้อมูลไม่ได้
สาเหตุ: Binance API มี rate limit หรือ server ช้าในบางช่วงเวลา
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
def create_session_with_retry():
"""สร้าง session ที่มี retry mechanism"""
session = requests.Session()
# ตั้งค่า retry strategy
retry_strategy = Retry(
total=5,
backoff_factor=2, # รอ 2, 4, 8, 16, 32 วินาที
status_forcelist=[429, 500, 502, 503, 504],
allowed_methods=["HEAD", "GET", "OPTIONS"]
)
adapter = HTTPAdapter(max_retries=retry_strategy)
session.mount("https://", adapter)
session.mount("http://", adapter)
return session
ใช้งาน
session = create_session_with_retry()
response = session.get(f"{BASE_URL}/api/v3/klines", params=params, timeout=30)
2. 401 Unauthorized - API Key ไม่ถูกต้อง
สาเหตุ: สำหรับ private API (เช่น account info) ต้องมี signature
import hmac
import hashlib
import requests
BINANCE_API_KEY = "YOUR_BINANCE_API_KEY"
BINANCE_SECRET_KEY = "YOUR_BINANCE_SECRET_KEY"
def get_account_info():
"""ดึงข้อมูลบัญชี Binance (ต้องใช้ signature)"""
endpoint = "/api/v3/account"
timestamp = int(time.time() * 1000)
# สร้าง query string
query_string = f"timestamp={timestamp}"
# สร้าง signature
signature = hmac.new(
BINANCE_SECRET_KEY.encode('utf-8'),
query_string.encode('utf-8'),
hashlib.sha256
).hexdigest()
# รวม query + signature
full_url = f"{BASE_URL}{endpoint}?{query_string}&signature={signature}"
headers = {
"X-MBX-APIKEY": BINANCE_API_KEY,
"Content-Type": "application/json"
}
response = requests.get(full_url, headers=headers)
if response.status_code == 200:
return response.json()
elif response.status_code == 401:
raise Exception("❌ 401 Unauthorized: ตรวจสอบ API Key และ Secret Key")
else:
raise Exception(f"❌ Error {response.status_code}: {response.text}")
หมายเหตุ: Public API (klines) ไม่ต้องมี signature
แค่ดึงข้อมูล OHLCV สามารถใช้วิธีด้านบนได้เลย
3. ข้อมูลซ้ำหรือหายหลังจากรันซ้ำ
สาเหตุ: การดึงข้อมูลซ้อนทับกันหรือ timestamp ไม่ตรง
def safe_fetch_and_save(symbol, interval, lookback_days=7):
"""
ดึงข้อมูลอย่างปลอดภัยพร้อมป้องกันข้อมูลซ้ำ
"""
init_database() # ตรวจสอบตาราง
# ดึง timestamp ล่าสุดที่มี
latest_ts = get_latest_timestamp(symbol, interval)
# กำหนดเวลาเริ่มต้น (7 วันก่อน timestamp ล่าสุด หรือ 7 วันก่อนปัจจุบัน)
if latest_ts:
start_time = latest_ts - (lookback_days * 24 * 60 * 60 * 1000)
start_dt = datetime.fromtimestamp(start_time / 1000)
else:
start_time = int((datetime.now() - timedelta(days=lookback_days)).timestamp() * 1000)
start_dt = datetime.fromtimestamp(start_time / 1000)
print(f"📥 ดึงข้อมูล {symbol} {interval} ตั้งแต่ {start_dt}")
# ดึงข้อมูล
df = get_klines(symbol, interval, start_time=start_time, limit=1000)
if df is not None and not df.empty:
# ลบข้อมูลเก่าที่ซ้ำก่อน insert
conn = sqlite3.connect(DB_PATH)
cursor = conn.cursor()
cursor.execute("""
DELETE FROM klines
WHERE symbol = ? AND interval = ? AND open_time >= ?
""", (symbol.upper(), interval, df["open_time"].iloc[0].isoformat()))
conn.commit()
conn.close()
# บันทึกข้อมูลใหม่ (UNIQUE constraint จะป้องกันข้อมูลซ้ำ)
save_to_database(df, symbol, interval)
print(f"✅ บันทึก {len(df)} candles สำเร็จ")
return df
การใรงานทุก 5 นาที
def job():
for symbol in ["BTCUSDT", "ETHUSDT"]:
for interval in ["1m", "5m", "15m"]:
safe_fetch_and_save(symbol, interval, lookback_days=1)
time.sleep(1)
schedule.every(5).minutes.do(job)
สรุป
การดึงและจัดเก็บข้อมูล K线 Binance อย่างมีประสิทธิภาพต้องอาศัย:
- การจัดการ Rate Limit: ใช้ retry mechanism และรอระหว่าง request
- การจัดเก็บที่ถูกต้อง: ใช้ UNIQUE constraint ป้องกันข้อมูลซ้ำ
- การดึงแบบ incremental: เก็บเฉพาะข้อมูลใหม่แทนดึงทั้งหมดทุกครั้ง
- การ backup: เก็บข้อมูลไว้เองไม่พึ่ง API 100%
สำหรับนักพัฒนาที่ต้องการนำข้อมูล K线 ไปประมวลผลด้วย AI เพื่อวิเคราะห์หรือสร้างรายงานอัตโนมัติ HolySheep AI เป็นตัวเลือกที่คุ้มค่าที่สุดในตลาด ด้วยราคาประหยัดกว่า 85% และความเร็วตอบสนองต่ำกว่า 50ms
👉 สมัคร HolySheep AI — รับเครดิตฟรีเมื่อลงทะเบียน