Chuyển tới nội dung chính

🐍 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:

Mở trên Colab

🔌 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.

EndpointDữ liệuCần key?
/api/v3/klinesNến OHLCV❌ Không
/api/v3/ticker/priceGiá hiện tại❌ Không
/api/v3/ticker/24hrThống kê 24 giờ❌ Không
/api/v3/depthOrder book❌ Không
/api/v3/tradesGiao dịch gần đây❌ Không
/api/v3/accountThô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/price mộ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ạnCon sốCách xử lý
Số nến tối đa mỗi request1000Phân trang bằng startTime
Weight mỗi phút1200Không gọi liên tục, dùng time.sleep
Số request mỗi giây~20Gọi theo lô, không gọi từng cặp
Dữ liệu lịch sửTuỳ cặpDù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.sleep và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


Xem video trên YouTube