🐍 Ngày 44 Python 365 ngày | Xây dựng bot giao dịch tự động với Binance
🗓 Ngày 44: Bot giao dịch tự động đầu tiên của bạn
Sau 43 ngày, bạn đã biết cách lấy dữ liệu giá, đọc order book, xử lý dữ liệu giao dịch bằng pandas. Hôm nay chúng ta ghép tất cả lại thành một bot giao dịch tự động thật sự — thứ có thể tự đọc giá, tự quyết định và tự đặt lệnh mà không cần bạn ngồi trước màn hình.
⚠️ Nguyên tắc an toàn số 1: Toàn bộ bài học này chạy trên Binance Testnet (tiền ảo, không phải tiền thật). Chỉ khi bot đã chạy ổn định nhiều tuần trên testnet, bạn mới nên nghĩ đến tiền thật — và khi đó hãy bắt đầu với số vốn nhỏ nhất có thể.
🎯 Mục tiêu bài học
Sau bài này, bạn sẽ:
- Hiểu kiến trúc tổng thể của một bot giao dịch tự động.
- Kết nối được Binance API bằng thư viện
python-binance. - Đặt được market order và limit order bằng code Python.
- Đọc được số dư tài khoản và trạng thái lệnh.
- Xây dựng vòng lặp bot cơ bản có xử lý lỗi.
- Biết cách chạy bot trên Testnet an toàn.
🧠 Bot giao dịch tự động là gì?
Bot giao dịch tự động (trading bot) là một chương trình chạy liên tục, thực hiện 4 việc theo vòng lặp:
| Bước | Công việc | Công cụ Python |
|---|---|---|
| 1️⃣ | Thu thập dữ liệu — lấy giá, nến, khối lượng | python-binance, REST API |
| 2️⃣ | Phân tích & ra quyết định — tính chỉ báo, xét điều kiện | pandas, numpy |
| 3️⃣ | Quản lý rủi ro — tính khối lượng lệnh, stop-loss | Logic Python thuần |
| 4️⃣ | Thực thi — đặt/hủy lệnh qua API | python-binance |
Vòng lặp này lặp lại mỗi vài giây hoặc mỗi khi nến mới đóng. Đó chính là toàn bộ bản chất của một bot.
flowchart LR
A[Thu thập dữ liệu] --> B[Phân tích tín hiệu]
B --> C{Có tín hiệu?}
C -->|Không| A
C -->|Có| D[Tính khối lượng + rủi ro]
D --> E[Đặt lệnh qua API]
E --> F[Ghi log / thông báo]
F --> A
🧰 Cài đặt công cụ
pip install python-binance pandas python-dotenv
Không bao giờ viết API key trực tiếp trong code. Hãy tạo file .env:
# .env — KHÔNG commit file này lên Git
BINANCE_API_KEY=your_testnet_api_key
BINANCE_API_SECRET=your_testnet_api_secret
BINANCE_TESTNET=True
Và thêm .env vào .gitignore:
echo ".env" >> .gitignore
🔑 Bước 1: Tạo API Key trên Binance Testnet
- Truy cập https://testnet.binance.vision/
- Đăng nhập bằng tài khoản GitHub.
- Bấm Generate HMAC_SHA256 Key.
- Sao chép API Key và Secret Key vào file
.env. - Testnet cho bạn sẵn số dư ảo (thường 10.000 USDT + một ít BTC/ETH) để thực hành.
🔌 Bước 2: Kết nối và kiểm tra tài khoản
import os
from dotenv import load_dotenv
from binance.client import Client
from binance.exceptions import BinanceAPIException
load_dotenv()
API_KEY = os.getenv("BINANCE_API_KEY")
API_SECRET = os.getenv("BINANCE_API_SECRET")
# testnet=True -> chạy trên tiền ảo
client = Client(API_KEY, API_SECRET, testnet=True)
# 1. Kiểm tra kết nối
status = client.get_system_status()
print("Trạng thái hệ thống:", status)
# 2. Xem số dư
account = client.get_account()
for asset in account["balances"]:
free = float(asset["free"])
if free > 0:
print(f"{asset['asset']}: {free}")
Nếu thấy danh sách số dư in ra, bạn đã kết nối thành công. 🎉
📊 Bước 3: Lấy giá và tính chỉ báo đơn giản
Bot cần biết giá hiện tại và tín hiệu. Ở đây ta dùng đường trung bình động (SMA) đơn giản — cầu nối từ các bài trước:
import pandas as pd
def get_klines(symbol="BTCUSDT", interval="1m", limit=100) -> pd.DataFrame:
"""Lấy nến và trả về DataFrame đã chuẩn hóa kiểu dữ liệu."""
klines = client.get_klines(symbol=symbol, interval=interval, limit=limit)
df = pd.DataFrame(klines, columns=[
"open_time", "open", "high", "low", "close", "volume",
"close_time", "quote_volume", "trades",
"taker_base", "taker_quote", "ignore",
])
for col in ["open", "high", "low", "close", "volume"]:
df[col] = df[col].astype(float)
df["open_time"] = pd.to_datetime(df["open_time"], unit="ms")
return df
def them_tin_hieu(df: pd.DataFrame, nhanh=9, cham=21) -> pd.DataFrame:
df["sma_nhanh"] = df["close"].rolling(nhanh).mean()
df["sma_cham"] = df["close"].rolling(cham).mean()
df["tin_hieu"] = 0
# Vàng cắt lên -> mua (1), cắt xuống -> bán (-1)
df.loc[df["sma_nhanh"] > df["sma_cham"], "tin_hieu"] = 1
df.loc[df["sma_nhanh"] < df["sma_cham"], "tin_hieu"] = -1
return df
df = them_tin_hieu(get_klines())
print(df[["open_time", "close", "sma_nhanh", "sma_cham", "tin_hieu"]].tail())
💡 Lưu ý về
klines: Mỗi phần tử nến gồm 12 giá trị, trong đó giá và khối lượng là chuỗi — bạn luôn phảiastype(float)trước khi tính toán, nếu khôngpandassẽ cộng chuỗi và cho kết quả sai.
💰 Bước 4: Tính khối lượng lệnh đúng cách
Sai lầm phổ biến nhất của người mới là đặt khối lượng tùy tiện. Hãy tính theo % rủi ro:
def tinh_khoi_luong(symbol, so_du_usdt, rui_ro_phan_tram=1.0, stop_loss_phan_tram=2.0):
"""Tính khối lượng (base asset) sao cho khi chạm stop-loss
chỉ mất đúng rui_ro_phan_tram % tổng số dư."""
gia = float(client.get_symbol_ticker(symbol=symbol)["price"])
so_tien_rui_ro = so_du_usdt * (rui_ro_phan_tram / 100)
khoang_cach_stop = gia * (stop_loss_phan_tram / 100)
khoi_luong = so_tien_rui_ro / khoang_cach_stop
return round(khoi_luong, 5), gia
khoi_luong, gia = tinh_khoi_luong("BTCUSDT", so_du_usdt=1000)
print(f"Giá BTC: {gia:,.2f} USDT | Khối lượng nên vào: {khoi_luong} BTC")
Công thức cốt lõi cần nhớ:
Khối lượng = (Số dư × % rủi ro) / (Giá × % stop-loss)
Ví dụ: số dư 1.000 USDT, rủi ro 1%, stop-loss 2%, giá BTC 60.000 USDT
Khối lượng = (1000 × 0.01) / (60000 × 0.02) = 10 / 1200 ≈ 0.00833 BTC
🚀 Bước 5: Đặt lệnh — Market và Limit
from binance.enums import (
SIDE_BUY, SIDE_SELL,
ORDER_TYPE_MARKET, ORDER_TYPE_LIMIT,
TIME_IN_FORCE_GTC,
)
# --- Lệnh MARKET: khớp ngay lập tức theo giá thị trường ---
def mua_market(symbol, khoi_luong):
return client.create_order(
symbol=symbol,
side=SIDE_BUY,
type=ORDER_TYPE_MARKET,
quantity=khoi_luong,
)
# --- Lệnh LIMIT: chỉ khớp khi giá chạm mức bạn muốn ---
def mua_limit(symbol, khoi_luong, gia_mong_muon):
return client.create_order(
symbol=symbol,
side=SIDE_BUY,
type=ORDER_TYPE_LIMIT,
timeInForce=TIME_IN_FORCE_GTC,
quantity=khoi_luong,
price=str(gia_mong_muon),
)
if __name__ == "__main__":
ket_qua = mua_market("BTCUSDT", 0.001)
print("Đã đặt lệnh:", ket_qua["orderId"], ket_qua["status"])
Phân biệt nhanh:
| Tiêu chí | Market Order | Limit Order |
|---|---|---|
| Khớp lệnh | Ngay lập tức | Khi giá chạm mức đặt |
| Kiểm soát giá | ❌ Không | ✅ Có |
| Rủi ro | Trượt giá (slippage) | Lệnh không khớp |
| Phù hợp | Vào/thoát gấp, stop-loss | Vào giá đẹp, chốt lời |
🛡️ Bước 6: Quản lý lệnh và vị thế
def xem_lenh_dang_mo(symbol="BTCUSDT"):
lenh = client.get_open_orders(symbol=symbol)
return [
{
"id": l["orderId"],
"huong": l["side"],
"gia": float(l["price"]),
"kl": float(l["origQty"]),
"da_khop": float(l["executedQty"]),
}
for l in lenh
]
def huy_lenh(symbol, order_id):
return client.cancel_order(symbol=symbol, orderId=order_id)
for l in xem_lenh_dang_mo():
print(l)
🤖 Bước 7: Vòng lặp bot hoàn chỉnh
Đây là phiên bản đầu tiên — đơn giản nhưng có đầy đủ xử lý lỗi, thứ mà 90% bot tự viết thiếu:
import time
import logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s | %(levelname)s | %(message)s",
handlers=[logging.FileHandler("bot.log", encoding="utf-8"), logging.StreamHandler()],
)
SYMBOL = "BTCUSDT"
vong_lap = 0
tin_hieu_truoc = 0
def lay_so_du_usdt():
for b in client.get_account()["balances"]:
if b["asset"] == "USDT":
return float(b["free"])
return 0.0
while True:
vong_lap += 1
try:
df = them_tin_hieu(get_klines(SYMBOL, "1m", 100))
tin_hieu_moi = int(df["tin_hieu"].iloc[-1])
gia = df["close"].iloc[-1]
logging.info(f"Vòng {vong_lap} | Giá {gia:,.2f} | Tín hiệu {tin_hieu_moi}")
# Chỉ hành động khi tín hiệu ĐỔI (tránh đặt lệnh liên tục)
if tin_hieu_moi != tin_hieu_truoc and tin_hieu_moi != 0:
if tin_hieu_moi == 1:
kl, _ = tinh_khoi_luong(SYMBOL, lay_so_du_usdt())
if kl > 0:
lenh = mua_market(SYMBOL, kl)
logging.info(f"✅ MUA {kl} {SYMBOL} (order {lenh['orderId']})")
else:
logging.info("📉 Tín hiệu bán — bỏ qua vì bot chưa hold vị thế")
tin_hieu_truoc = tin_hieu_moi
except BinanceAPIException as e:
logging.error(f"Lỗi API Binance: {e.message} (code {e.code})")
except Exception as e:
logging.exception(f"Lỗi không mong đợi: {e}")
time.sleep(60) # nghỉ 60 giây rồi lặp lại
Ba điểm sống còn trong đoạn code trên:
try/exceptbao quanh toàn bộ thân vòng lặp — một lỗi mạng không được phép làm bot chết.time.sleep(60)— gọi API liên tục sẽ bị Binance chặn (rate limit) hoặc khóa key.- Chỉ giao dịch khi tín hiệu đổi — nếu không, bot sẽ mua 60 lần mỗi giờ.
🧪 Bài tập thực hành
- In ra 20 nến gần nhất cùng
sma_nhanh,sma_chamdưới dạng bảng bằngpandas. - Đổi
intervalsang"5m"và"1h", xem tín hiệu thay đổi thế nào. - Viết hàm
dat_stop_loss(symbol, gia_mua, phan_tram=2)đặt một STOP_MARKET order cách giá mua 2%. - Ghi log số dư USDT sau mỗi 10 vòng lặp vào file
so_du.csv. - Thử phá bot: tắt mạng giữa chừng, xem bot có tự phục hồi không? Nếu chết, sửa lại.
⚠️ Lỗi thường gặp và cách xử lý
| Lỗi | Nguyên nhân | Cách sửa |
|---|---|---|
Invalid API-key, IP, or permissions | Chưa bật quyền Spot Trading cho key | Vào Testnet → API Management → bật Spot |
MIN_NOTIONAL | Giá trị lệnh quá nhỏ | Tăng khối lượng; kiểm tra client.get_symbol_info() |
LOT_SIZE | Khối lượng sai bước (step size) | Làm tròn theo stepSize của symbol |
Timestamp for this request is outside of the recvWindow | Đồng hồ máy lệch | Bật đồng bộ giờ hệ thống |
| Bot "mua liên tục" | Thiếu kiểm tra tín hiệu đổi | Lưu tin_hieu_truoc như ví dụ trên |
📝 Ghi chú quan trọng
- Testnet ≠ Mainnet. Testnet có thanh khoản mỏng, giá đôi khi lệch; kết quả trên testnet không đảm bảo trên tiền thật.
- Tuyệt đối không hard-code API key. Nếu vô tình push key lên GitHub, hãy thu hồi (revoke) ngay lập tức.
- Không bật quyền Withdraw cho API key. Bot chỉ cần Spot Trading, không bao giờ cần rút tiền.
- Log là bạn thân nhất của bạn. Bot chạy 24/7, bạn không thể ngồi nhìn — log là cách duy nhất biết nó đang làm gì.
- Backtest trước, chạy thật sau. Một chiến lược chưa backtest thì chưa đáng để tin.
⚖️ Miễn trừ trách nhiệm: Nội dung này chỉ nhằm mục đích giáo dục về lập trình. Giao dịch tài sản số có rủi ro rất cao và có thể mất toàn bộ vốn. Hãy tự nghiên cứu và chỉ dùng tiền bạn có thể chấp nhận mất.
🎯 Tóm tắt ngày 44
Bạn vừa xây xong bot giao dịch tự động đầu tiên:
✅ Kết nối Binance API an toàn qua .env
✅ Lấy nến, tính chỉ báo SMA bằng pandas
✅ Tính khối lượng theo % rủi ro
✅ Đặt lệnh market/limit, quản lý lệnh mở
✅ Vòng lặp bot có xử lý lỗi và ghi log