
Phân tích dữ liệu với Pandas, NumPy và Matplotlib
Làm sạch, chuyển đổi và trực quan hóa dữ liệu tài chính như một quant
NumPy: Động cơ bên dưới
Mọi phép tính tài chính nghiêm túc trong Python cuối cùng đều chạy qua NumPy. Trong khi các list Python linh hoạt, chúng lại chậm — mỗi phần tử là một đối tượng Python đầy đủ với chi phí kiểm tra kiểu dữ liệu. Các mảng NumPy lưu trữ các số thô trong các khối bộ nhớ liên tục, cho phép các phép toán chạy nhanh hơn 100 lần so với các vòng lặp Python thuần.
Đây là sự khác biệt trong thực tế:
import numpy as np
# Tạo một mảng gồm 1 triệu lợi suất hàng ngày ngẫu nhiên
returns = np.random.normal(0.0005, 0.02, 1_000_000)
# Các phép toán vector hóa — không cần vòng lặp
cumulative = np.cumprod(1 + returns) # Tăng trưởng của 1 đô la
sharpe = returns.mean() / returns.std() * np.sqrt(252)
max_drawdown = np.min(cumulative / np.maximum.accumulate(cumulative) - 1)
print(f"Sharpe Ratio: {sharpe:.3f}")
print(f"Max Drawdown: {max_drawdown:.2%}")
Các khái niệm NumPy chính cho tài chính:
- Phép toán vector hóa — Áp dụng toán học cho toàn bộ mảng cùng lúc thay vì lặp từng phần tử.
- Broadcasting — Tự động căn chỉnh các mảng có shape khác nhau cho phép toán số học (ví dụ: trừ một trung bình vô hướng từ mọi phần tử).
- Đại số tuyến tính —
np.linalgcung cấp phép nhân ma trận, phân tích trị riêng, và các bộ giải dùng trong tối ưu hóa danh mục đầu tư. - Lấy mẫu ngẫu nhiên — Mô phỏng Monte Carlo, lấy mẫu bootstrap, và mô hình hóa ngẫu nhiên đều dựa vào
np.random.
Mẹo hiệu năng: Luôn ưu tiên các phép toán NumPy vector hóa hơn các vòng lặp for của Python. Một phép tối ưu hóa danh mục đầu tư trên 500 tài sản tốn 30 giây với vòng lặp có thể hoàn thành trong dưới 50 milli giây với NumPy vector hóa — tăng tốc gấp 600 lần.
Pandas: DataFrame cho dữ liệu tài chính
Nếu NumPy là động cơ, pandas là bảng điều khiển. Nó bọc các mảng NumPy trong các cấu trúc có nhãn, có index khiến việc xử lý dữ liệu tài chính trở nên trực quan và biểu cảm.
Hai cấu trúc cốt lõi là:
- Series — Một cột dữ liệu duy nhất với một index (hãy nghĩ: một chuỗi thời gian của giá đóng cửa).
- DataFrame — Một bảng gồm các cột, mỗi cột là một Series, chia sẻ chung một index (hãy nghĩ: dữ liệu nến OHLCV).
Việc nạp và kiểm tra dữ liệu rất đơn giản:
import pandas as pd
# Đọc từ CSV
df = pd.read_csv("btc_daily.csv", parse_dates=["date"], index_col="date")
# Hoặc từ JSON (phổ biến trong các phản hồi API)
df = pd.read_json("https://api.example.com/candles?symbol=ETH")
# Kiểm tra nhanh
print(df.shape) # (365, 5)
print(df.dtypes) # Kiểu của các cột
print(df.describe()) # Tóm tắt thống kê
print(df.tail()) # 5 dòng cuối
Điều làm cho pandas không thể thiếu trong tài chính là DatetimeIndex. Khi index của bạn có kiểu datetime, bạn mở khóa được các phép toán chuỗi thời gian mạnh mẽ:
# Cắt dữ liệu theo khoảng ngày
q1_data = df["2025-01":"2025-03"]
# Chuyển đổi dữ liệu hàng ngày sang OHLC hàng tuần
weekly = df["close"].resample("W").ohlc()
# Điền tiến các dữ liệu thiếu (cuối tuần, ngày lễ)
df = df.asfreq("D").ffill()
Các phép toán này xử lý những thực tế lộn xộn của dữ liệu tài chính — các khoảng trống, sự khác biệt múi giờ, dấu thời gian không đều — để bạn có thể tập trung vào việc phân tích thay vì xử lý dữ liệu vặt.
Tính toán cửa sổ trượt: Đường trung bình động và độ biến động
Phân tích tài chính về bản chất là theo cửa sổ. Bạn hiếm khi quan tâm đến một điểm dữ liệu đơn lẻ một mình — bối cảnh đến từ cách nó liên quan đến lịch sử gần đây. Phương thức .rolling() của pandas là công cụ chính của bạn ở đây.
import pandas as pd
import numpy as np
# Giả sử df có một cột "close" với DatetimeIndex
df["sma_20"] = df["close"].rolling(20).mean()
df["sma_50"] = df["close"].rolling(50).mean()
df["ema_12"] = df["close"].ewm(span=12).mean()
# Lợi suất hàng ngày và độ biến động cửa sổ trượt
df["returns"] = df["close"].pct_change()
df["vol_30d"] = df["returns"].rolling(30).std() * np.sqrt(365)
# Dải Bollinger
df["bb_upper"] = df["sma_20"] + 2 * df["close"].rolling(20).std()
df["bb_lower"] = df["sma_20"] - 2 * df["close"].rolling(20).std()
# Tương quan cửa sổ trượt giữa hai tài sản
df["corr_btc_eth"] = df["btc_returns"].rolling(60).corr(df["eth_returns"])
Các phép tính cửa sổ trượt chính mà mọi trader nên biết:
- Đường trung bình động đơn giản (SMA) — Trung bình có trọng số bằng nhau qua N kỳ. Trễ nhưng ổn định.
- Đường trung bình động hàm số mũ (EMA) — Cho trọng số cao hơn cho dữ liệu gần đây qua
.ewm(). Nhạy hơn với các thay đổi giá. - Độ biến động cửa sổ trượt — Độ lệch chuẩn của lợi suất qua một cửa sổ, được thường niên hóa. Thiết yếu cho việc định cỡ vị thế và ngân sách rủi ro.
- Tương quan cửa sổ trượt — Đo cách hai tài sản di chuyển cùng nhau theo thời gian. Quan trọng cho đa dạng hóa danh mục đầu tư.
Trên các nền tảng như GaiaEx, bạn có thể lấy dữ liệu nến lịch sử qua API giao dịch, nạp nó vào một DataFrame pandas, và tính toán các chỉ báo này trong vài giây — tất cả trong khi tài sản của bạn vẫn an toàn trong ví MPC của bạn.
GroupBy và tổng hợp cho phân tích danh mục đầu tư
Khi bạn quản lý nhiều tài sản, .groupby() trở nên thiết yếu. Nó cho phép bạn chia dữ liệu theo danh mục, áp dụng các phép tính, và kết hợp kết quả — mẫu split-apply-combine (chia-áp dụng-kết hợp).
# DataFrame với nhiều tài sản
trades = pd.DataFrame({
"symbol": ["BTC", "ETH", "BTC", "SOL", "ETH", "BTC"],
"side": ["buy", "buy", "sell", "buy", "sell", "buy"],
"pnl": [120.5, -45.2, 89.0, 210.3, 55.8, -30.1],
"volume": [5000, 3200, 4800, 1500, 2900, 5100],
})
# Hiệu suất theo từng tài sản
summary = trades.groupby("symbol").agg(
total_pnl=("pnl", "sum"),
avg_pnl=("pnl", "mean"),
trade_count=("pnl", "count"),
total_volume=("volume", "sum"),
win_rate=("pnl", lambda x: (x > 0).mean()),
)
print(summary.sort_values("total_pnl", ascending=False))
Các mẫu tổng hợp nâng cao cho công việc danh mục đầu tư:
- GroupBy nhiều cấp — Nhóm theo symbol và side để xem hiệu suất long so với short trên mỗi tài sản.
- Các hàm tổng hợp tùy chỉnh — Tính tỷ số Sharpe, tỷ số Sortino, hoặc drawdown tối đa trên mỗi nhóm tài sản.
- Bảng pivot — Tái định hình dữ liệu để xem lợi suất theo tháng theo từng tài sản ở dạng thân thiện với bản đồ nhiệt bằng
pd.pivot_table(). - Resample kết hợp với groupby — Kết hợp resample dựa trên thời gian với nhóm phân loại cho phân tích chuỗi thời gian đa tài sản.
Matplotlib: Vẽ biểu đồ hành vi giá và tín hiệu
Dữ liệu không có trực quan hóa chỉ là những con số. Matplotlib biến phân tích của bạn thành các biểu đồ tiết lộ những mô hình vô hình trong dữ liệu thô.
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
fig, axes = plt.subplots(3, 1, figsize=(14, 10),
sharex=True, gridspec_kw={"height_ratios": [3, 1, 1]})
# Giá + Đường trung bình động
axes[0].plot(df.index, df["close"], label="Close", linewidth=1.2)
axes[0].plot(df.index, df["sma_20"], label="SMA 20", linestyle="--")
axes[0].fill_between(df.index, df["bb_upper"], df["bb_lower"],
alpha=0.1, color="blue", label="Bollinger Bands")
axes[0].set_ylabel("Price (USD)")
axes[0].legend(loc="upper left")
# Cột khối lượng
colors = ["green" if c > o else "red"
for c, o in zip(df["close"], df["open"])]
axes[1].bar(df.index, df["volume"], color=colors, alpha=0.7)
axes[1].set_ylabel("Volume")
# Độ biến động cửa sổ trượt
axes[2].plot(df.index, df["vol_30d"], color="purple")
axes[2].set_ylabel("30d Volatility")
axes[2].axhline(y=0.8, color="red", linestyle=":", alpha=0.5)
axes[2].xaxis.set_major_formatter(mdates.DateFormatter("%b %Y"))
plt.tight_layout()
plt.savefig("analysis_dashboard.png", dpi=150)
plt.show()
Điều này tạo ra một bảng điều khiển ba panel chuyên nghiệp: hành vi giá với dải Bollinger ở trên, khối lượng ở giữa, và độ biến động ở dưới. Bố cục này phản chiếu những gì bạn sẽ thấy trên các terminal giao dịch chuyên nghiệp.
Đối với trực quan hóa thống kê, seaborn xây dựng trên Matplotlib với các hàm cấp cao hơn:
import seaborn as sns
# Phân phối lợi suất
sns.histplot(df["returns"].dropna(), bins=100, kde=True)
# Bản đồ nhiệt tương quan giữa các tài sản
corr_matrix = portfolio_returns.corr()
sns.heatmap(corr_matrix, annot=True, cmap="RdYlGn", center=0)Kết hợp mọi thứ: Một pipeline phân tích hoàn chỉnh
Sức mạnh thực sự xuất hiện khi bạn kết chuỗi các công cụ này thành một pipeline có thể lặp lại. Đây là một quy trình làm việc mà các quant chuyên nghiệp dùng hàng ngày:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
def analyze_asset(symbol: str, df: pd.DataFrame) -> dict:
"""Pipeline phân tích đầy đủ cho một tài sản duy nhất."""
df["returns"] = df["close"].pct_change()
df["log_returns"] = np.log(df["close"] / df["close"].shift(1))
return {
"symbol": symbol,
"total_return": (df["close"].iloc[-1] / df["close"].iloc[0]) - 1,
"annual_vol": df["returns"].std() * np.sqrt(365),
"sharpe": df["returns"].mean() / df["returns"].std() * np.sqrt(365),
"max_drawdown": (df["close"] / df["close"].cummax() - 1).min(),
"skewness": df["returns"].skew(),
"kurtosis": df["returns"].kurtosis(),
}
# Phân tích nhiều tài sản
results = [analyze_asset(sym, data) for sym, data in assets.items()]
summary = pd.DataFrame(results).set_index("symbol")
print(summary.round(4))
Pipeline này lấy dữ liệu giá thô và tạo ra một hồ sơ rủi ro-lợi suất toàn diện cho mỗi tài sản. Từ đây, bạn có thể đưa các chỉ số này vào một bộ tối ưu hóa danh mục đầu tư, tạo các đề xuất phân bổ, hoặc kích hoạt các tín hiệu tái cân bằng.
Bộ công cụ pandas-NumPy-Matplotlib là nền tảng mà mọi thứ khác trong tài chính Python xây dựng dựa trên. Nắm vững ba thư viện này và bạn sẽ có kỹ năng để phân tích bất kỳ thị trường nào, xây dựng bất kỳ chỉ báo nào, và trực quan hóa bất kỳ chiến lược nào — bất kể bạn đang giao dịch cổ phiếu trên NYSE, crypto trên GaiaEx, hay phái sinh trên CME.

