🐍 Ngày 39 - Python hằng ngày 365 ngày - Làm việc với dữ liệu Binance bằng Python
📊 Làm việc với Dữ liệu Binance bằng Python
Trong ngày hôm nay, chúng ta sẽ tìm hiểu cách kết nối với sàn giao dịch Binance để lấy dữ liệu thị trường và phân tích bằng Python. Đây là một bước quan trọng trong các dự án liên quan đến tài chính, trading bot, hoặc theo dõi giá crypto tự động.
✅ Mục tiêu
- Cài đặt thư viện
python-binance - Kết nối API với Binance
- Lấy dữ liệu nến (OHLCV) từ thị trường BTC/USDT
- Phân tích cơ bản và hiển thị
🧰 Cài đặt thư viện cần thiết
pip install python-binance pandas
🔑 Kết nối API Binance
Bạn cần đăng ký API key trên Binance để sử dụng.
from binance.client import Client
import pandas as pd
# Điền API key & secret
api_key = 'YOUR_API_KEY'
api_secret = 'YOUR_API_SECRET'
client = Client(api_key, api_secret)
📈 Lấy dữ liệu nến 1 giờ của cặp BTC/USDT trong 7 ngày
klines = client.get_historical_klines("BTCUSDT", Client.KLINE_INTERVAL_1HOUR, "7 day ago UTC")
# Tạo DataFrame
df = pd.DataFrame(klines, columns=[
'timestamp', 'open', 'high', 'low', 'close', 'volume',
'close_time', 'quote_asset_volume', 'number_of_trades',
'taker_buy_base', 'taker_buy_quote', 'ignore'
])
# Chuyển định dạng timestamp
df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')
df.set_index('timestamp', inplace=True)
# Chỉ giữ lại các cột cần thiết
df = df[['open', 'high', 'low', 'close', 'volume']].astype(float)
print(df.head())
📊 Hiển thị biểu đồ giá đơn giản
import matplotlib.pyplot as plt
df['close'].plot(figsize=(12,6), title='BTC/USDT - Close Price (1h)')
plt.xlabel('Time')
plt.ylabel('Price (USDT)')
plt.grid(True)
plt.show()
🎯 Gợi ý mở rộng
- Tạo script theo dõi giá theo thời gian thực
- Lưu dữ liệu vào CSV hoặc SQLite
- Kết hợp với chiến lược giao dịch tự động (Python Bot)
- Hiển thị dữ liệu bằng Dash/Streamlit
💡 Tổng kết
Hôm nay bạn đã biết cách:
✅ Kết nối với Binance
✅ Lấy dữ liệu giá theo khung thời gian
✅ Xử lý và hiển thị bằng pandas & matplotlib
Hẹn gặp bạn ở Ngày 40, nơi chúng ta sẽ học cách gửi tín hiệu đến Telegram hoặc Zalo để theo dõi thị trường tự động 🚀
Bạn có thể mở bài học này trên Google Colab để chạy trực tiếp:
🔌 Binance API — không cần API key vẫn lấy được dữ liệu
Tin tốt: hầu hết endpoint dữ liệu công khai của Binance không cần API key. Bạn chỉ cần key khi giao dịch hoặc đọc thông tin tài khoản.
| Endpoint | Dữ liệu | Cần key? |
|---|---|---|
/api/v3/klines | Nến OHLCV | ❌ Không |
/api/v3/ticker/price | Giá hiện tại | ❌ Không |
/api/v3/ticker/24hr | Thống kê 24 giờ | ❌ Không |
/api/v3/depth | Order book | ❌ Không |
/api/v3/trades | Giao dịch gần đây | ❌ Không |
/api/v3/account | Thông tin tài khoản | ✅ Có |
/api/v3/order | Đặt lệnh | ✅ Có |
import urllib.request
import json
import pandas as pd
BINANCE = "https://api.binance.com/api/v3"
def goi_api(duong_dan: str, tham_so: dict | None = None) -> dict | list:
"""Gọi API Binance không cần thư viện ngoài."""
url = BINANCE + duong_dan
if tham_so:
url += "?" + "&".join(f"{k}={v}" for k, v in tham_so.items())
with urllib.request.urlopen(url, timeout=15) as r:
return json.loads(r.read().decode())
# Lấy giá hiện tại của nhiều cặp cùng lúc
gia = goi_api("/ticker/price")
bang_gia = {item["symbol"]: float(item["price"]) for item in gia}
for cap in ("BTCUSDT", "ETHUSDT", "SOLUSDT"):
if cap in bang_gia:
print(f"{cap}: {bang_gia[cap]:,.2f} USDT")
💡 Gọi
ticker/pricemột lần lấy toàn bộ cặp (hơn 2000 cặp) nhanh hơn nhiều so với gọi từng cặp. Khi làm việc với API, luôn tìm endpoint trả nhiều dữ liệu trong một request.
📊 Chuyển dữ liệu API thành DataFrame
import pandas as pd
def lay_nen(symbol: str = "BTCUSDT", interval: str = "1h",
limit: int = 500) -> pd.DataFrame:
"""Lấy nến OHLCV từ Binance và chuyển thành DataFrame."""
du_lieu = goi_api("/klines", {
"symbol": symbol, "interval": interval, "limit": limit,
})
df = pd.DataFrame(du_lieu, columns=[
"open_time", "open", "high", "low", "close", "volume",
"close_time", "quote_volume", "so_giao_dich",
"taker_buy_base", "taker_buy_quote", "bo_qua",
])
# --- BẮT BUỘC: API trả mọi thứ dưới dạng CHUỖI ---
for cot in ("open", "high", "low", "close", "volume", "quote_volume"):
df[cot] = df[cot].astype(float)
df["so_giao_dich"] = df["so_giao_dich"].astype(int)
# --- Đổi mốc thời gian từ mili-giây sang datetime ---
df["thoi_gian"] = pd.to_datetime(df["open_time"], unit="ms")
df = df.set_index("thoi_gian")
return df[["open", "high", "low", "close", "volume", "so_giao_dich"]]
df = lay_nen("BTCUSDT", "1h", 500)
print(df.head())
print(f"\n{len(df)} nến | từ {df.index[0]} đến {df.index[-1]}")
⚠️ Lỗi phổ biến nhất khi làm việc với API: quên chuyển đổi kiểu dữ liệu. API trả
"67234.56000000"(chuỗi), không phải số. Nếu bạn cộng trực tiếp, Python sẽ ghép chuỗi và cho kết quả vô nghĩa. Luôn.astype(float)ngay sau khi tạo DataFrame.
📈 Phân tích nhanh dữ liệu vừa lấy
import numpy as np
# --- 1. Chỉ báo cơ bản ---
df["sma20"] = df["close"].rolling(20).mean()
df["sma50"] = df["close"].rolling(50).mean()
# --- 2. Biến động ---
df["log_return"] = np.log(df["close"] / df["close"].shift(1))
vol_ngay = df["log_return"].std()
vol_nam = vol_ngay * np.sqrt(24 * 365) # 24 nến/ngày với khung 1h
print(f"Biến động năm hoá: {vol_nam:.1%}")
# --- 3. ATR (Average True Range) ---
tr = pd.concat([
df["high"] - df["low"],
(df["high"] - df["close"].shift()).abs(),
(df["low"] - df["close"].shift()).abs(),
], axis=1).max(axis=1)
df["atr14"] = tr.rolling(14).mean()
# --- 4. Thống kê mô tả ---
print(f"\nGiá cao nhất 500 nến: {df['high'].max():,.2f}")
print(f"Giá thấp nhất: {df['low'].min():,.2f}")
print(f"ATR hiện tại: {df['atr14'].iloc[-1]:,.2f} "
f"({df['atr14'].iloc[-1] / df['close'].iloc[-1]:.2%} giá)")
print(f"Khối lượng TB: {df['volume'].mean():,.2f}")
# --- 5. Xu hướng đơn giản ---
dong = df.iloc[-1]
if dong["sma20"] > dong["sma50"]:
print("\n📈 Xu hướng: TĂNG (SMA20 > SMA50)")
else:
print("\n📉 Xu hướng: GIẢM (SMA20 < SMA50)")
🗄️ Giới hạn của API và cách xử lý
| Giới hạn | Con số | Cách xử lý |
|---|---|---|
| Số nến tối đa mỗi request | 1000 | Phân trang bằng startTime |
| Weight mỗi phút | 1200 | Không gọi liên tục, dùng time.sleep |
| Số request mỗi giây | ~20 | Gọi theo lô, không gọi từng cặp |
| Dữ liệu lịch sử | Tuỳ cặp | Dùng startTime để lấy xa hơn |
⚠️ Binance sẽ chặn IP nếu bạn vượt giới hạn. Nếu gặp lỗi HTTP 418 hoặc 429, bạn đã bị chặn tạm thời. Hãy chờ (thời gian chờ tăng dần mỗi lần vi phạm) và thêm
time.sleepvào code.
Lấy dữ liệu nhiều hơn 1000 nến
import time
def lay_nen_lich_su(symbol: str, interval: str, so_nen: int) -> pd.DataFrame:
"""Lấy nhiều hơn 1000 nến bằng cách phân trang."""
tat_ca = []
ket_thuc = None # None = lấy từ hiện tại lùi về
while len(tat_ca) < so_nen:
tham_so = {
"symbol": symbol,
"interval": interval,
"limit": min(1000, so_nen - len(tat_ca)),
}
if ket_thuc:
tham_so["endTime"] = ket_thuc
lo = goi_api("/klines", tham_so)
if not lo:
break
tat_ca = lo + tat_ca # thêm vào ĐẦU
ket_thuc = lo[0][0] - 1 # lùi tiếp về trước
time.sleep(0.3) # tôn trọng rate limit
df = pd.DataFrame(tat_ca, columns=[
"open_time", "open", "high", "low", "close", "volume",
"close_time", "quote_volume", "so_giao_dich",
"taker_buy_base", "taker_buy_quote", "bo_qua",
])
for cot in ("open", "high", "low", "close", "volume"):
df[cot] = df[cot].astype(float)
df["thoi_gian"] = pd.to_datetime(df["open_time"], unit="ms")
return df.set_index("thoi_gian")[["open", "high", "low", "close", "volume"]]
📝 Bài tập mở rộng
Bài 1: Bảng giá nhiều cặp
Lấy giá 10 cặp crypto phổ biến, tính % thay đổi 24 giờ, in ra bảng xếp hạng.
Bài 2: So sánh biến động
Tính biến động (độ lệch chuẩn log return) của BTC, ETH, SOL trong 30 ngày. Cặp nào biến động mạnh nhất?
Bài 3: Tìm nến có khối lượng đột biến
Tìm những nến có khối lượng cao hơn 3 lần trung bình 50 nến gần nhất.
Bài 4: Lưu dữ liệu
Lấy dữ liệu và lưu ra CSV/Parquet. Lần sau đọc từ file thay vì gọi API lại.
🎯 Tóm tắt ngày 39
✅ Gọi Binance API không cần API key cho dữ liệu công khai
✅ Chuyển JSON thành DataFrame — luôn .astype(float)
✅ Đổi open_time (ms) thành DatetimeIndex
✅ Tính SMA, ATR, biến động từ dữ liệu thật
✅ Hiểu giới hạn rate limit và phân trang lấy dữ liệu lịch sử
✅ Lưu dữ liệu ra file để không gọi lại API
