Xây Dựng Thuật Toán Tạo Lập Thị Trường Cho Các Cặp Tiền Điện Tử Sử Dụng Mô Hình Avellaneda-Stoikov
Xin chào các bạn! Hôm nay tôi sẽ hướng dẫn cách xây dựng thuật toán tạo lập thị trường cho các cặp tiền điện tử USD+/wETH và USD+/cbbtc. Chúng ta sẽ sử dụng mô hình Avellaneda-Stoikov (A-S) và nâng cấp nó với thuật toán Học Tăng Cường (PPO) để tối ưu hóa spread động. Nghe có vẻ phức tạp? Đừng lo, tôi sẽ phân tích mọi thứ thành các bước rõ ràng để ngay cả một lập trình viên mới bắt đầu cũng có thể theo dõi.
Trực quan hóa bản chất của tạo lập thị trường: liên tục cân bằng lệnh mua và bán xung quanh một mức giá hợp lý để tổng hợp thanh khoản thị trường tối ưu.
Tạo Lập Thị Trường Là Gì Và Tại Sao Chúng Ta Cần Nó?
Tạo lập thị trường là một chiến lược trong đó nhà giao dịch đồng thời đặt lệnh mua và bán cho một tài sản, kiếm lợi nhuận từ spread (chênh lệch giữa các mức giá). Trong không gian DeFi, các nhà tạo lập thị trường đóng vai trò quan trọng bằng cách cung cấp thanh khoản và giảm slippage cho các bên tham gia thị trường khác.
Hãy tưởng tượng bạn là một người bán hàng tại chợ, luôn sẵn sàng mua hàng hóa với giá thấp hơn thị trường một chút và bán với giá cao hơn một chút. Lợi nhuận của bạn là chênh lệch giữa giá mua và giá bán. Nhưng có một điểm mấu chốt: nếu giá đột ngột di chuyển theo một chiều, bạn có thể tích lũy quá nhiều tồn kho hoặc ngược lại, không còn gì để bán.
Mô Hình Avellaneda-Stoikov: Toán Học Phục Vụ Giao Dịch
Mô hình A-S là một phương pháp toán học để xác định giá tối ưu cho việc tạo lập thị trường. Ưu điểm chính của nó là xem xét không chỉ giá thị trường hiện tại mà còn cả quy mô vị thế của bạn (tồn kho), biến động thị trường và khẩu vị rủi ro.
Các công thức chính của mô hình:
δ_a = S_t + (1/γ) * ln(1 + γ/k) + q_t * σ² * T
δ_b = S_t - (1/γ) * ln(1 + γ/k) - q_t * σ² * T
trong đó:
δ_avàδ_blà giá ask và bidS_tlà giá thị trường hiện tạiγlà tham số rủi ro (càng cao, spread càng rộng)klà tốc độ đến của lệnhq_tlà tồn kho hiện tạiσlà độ biến độngTlà khung thời gian
Các Tính Năng Giao Dịch Onchain
Khi chúng ta đưa thuật toán lên onchain, các thách thức bổ sung xuất hiện:
- Độ trễ – các giao dịch trên blockchain không tức thời, và giá có thể thay đổi trước khi lệnh được thực thi
- Chi phí gas – mỗi giao dịch đòi hỏi phí mạng
- Đặc thù AMM/PMM – cơ chế pool thanh khoản khác với các sàn giao dịch truyền thống
Hãy cùng xem cách tính đến những yếu tố này trong thuật toán của chúng ta.
Bước 1: Thiết Lập Môi Trường Và Thu Thập Dữ Liệu
Đầu tiên, chúng ta cần thiết lập môi trường để lấy dữ liệu thị trường. Chúng ta sẽ sử dụng Binance API để lấy giá hiện tại và độ sâu order book.
std::tuple MarketMaker::get_binance_data(const std::string& pair) {
// In real code, this would be a request to the Binance API
// Returns: mid_price, bid, ask, bid_volume, ask_volume
double mid_price = 2000.0;
double bid = mid_price - 1.0;
double ask = mid_price + 1.0;
double bid_volume = 10.0;
double ask_volume = 8.0;
return {mid_price, bid, ask, bid_volume, ask_volume};
}
Chúng ta cũng sẽ cần các chỉ số onchain như chi phí gas và độ trễ mạng:
std::pair MarketMaker::get_onchain_metrics() {
// In real code, this would be a request to an Ethereum node
// Returns: gas_price (wei), latency (seconds)
return {50e9, 12.0};
}
Bước 2: Triển Khai Mô Hình A-S Cơ Bản
Bây giờ hãy triển khai tính toán spread sử dụng mô hình A-S:
std::pair MarketMaker::calculate_spreads(double S_t, double sigma, double k, double q_t) {
// Avellaneda-Stoikov formula
double spread_term = (1.0 / gamma_) * log(1.0 + gamma_ / k);
double inventory_term = q_t * sigma * sigma * T_;
double delta_a = S_t + spread_term + inventory_term; // Ask price
double delta_b = S_t - spread_term - inventory_term; // Bid price
return {delta_a, delta_b};
}
Lưu ý inventory_term. Nếu bạn có tồn kho dương (nhiều tài sản), giá ask giảm và giá bid giảm nhiều hơn để khuyến khích bán và hạn chế mua. Và ngược lại đối với tồn kho âm.
Bước 3: Điều Chỉnh Mô Hình Cho Giao Dịch Onchain
Bây giờ chúng ta cần tính đến các đặc thù blockchain. Hãy bắt đầu với độ trễ:
double MarketMaker::adjust_price_with_latency(double S_t, double sigma, double latency) {
// Simulate random price change due to latency
double latency_adjustment = utils::normal_dist(0.0, sigma * std::sqrt(latency));
return S_t + latency_adjustment;
}
Ở đây chúng ta sử dụng mô hình random walk: độ biến động càng cao và độ trễ càng dài, giá càng có thể thay đổi nhiều trước khi lệnh được thực thi.
Bây giờ hãy tính đến chi phí gas:
double MarketMaker::calculate_gas_cost(double gas_price, double trade_size) {
const double GAS_LIMIT_PER_ORDER = 100000; // Approximate value per order
return (gas_price * GAS_LIMIT_PER_ORDER * trade_size) / 1e18; // Convert wei to ETH
}
Cuối cùng, hãy điều chỉnh spread cho các đặc thù pool PMM:
std::pair MarketMaker::adjust_spreads_for_pmm(double S_t, double delta_a, double delta_b, double pool_depth) {
// Simplified PMM model: adjust spreads based on pool depth
const double MIN_POOL_DEPTH = 10.0;
double depth_factor = std::max(pool_depth, MIN_POOL_DEPTH) / MIN_POOL_DEPTH;
// Reduce spreads with greater pool depth
double spread_reduction = 1.0 / std::sqrt(depth_factor);
double mid_price = (delta_a + delta_b) / 2;
double new_delta_a = mid_price + (delta_a - mid_price) * spread_reduction;
double new_delta_b = mid_price - (mid_price - delta_b) * spread_reduction;
return {new_delta_a, new_delta_b};
}
Bước 4: Quản Lý Tồn Kho
Để theo dõi và quản lý tồn kho, hãy tạo một lớp đơn giản:
class InventoryManager {
public:
InventoryManager() : inventory_(0.0) {}
void update_inventory(double size, bool is_buy) {
inventory_ += is_buy ? size : -size;
}
double get_inventory() const {
return inventory_;
}
private:
double inventory_;
};
Trực quan hóa Rủi ro Tồn kho: Theo dõi quy mô vị thế để tránh tiếp xúc quá mức (dài hoặc ngắn) với các biến động thị trường một chiều.
Bước 5: Kết Hợp Tất Cả Thành Một Thuật Toán Duy Nhất
Bây giờ hãy kết hợp tất cả các thành phần thành một thuật toán tạo lập thị trường duy nhất:
void MarketMaker::step(double S_t, double sigma, double k, double latency, double gas_cost, double trade_size) {
// Get current inventory
double current_inventory = inventory_.get_inventory();
// Calculate spreads based on current market conditions and inventory
auto [delta_a, delta_b] = calculate_spreads(S_t, sigma, k, current_inventory);
auto [adjusted_delta_a, adjusted_delta_b] = adjust_spreads_for_onchain(S_t, delta_a, delta_b, latency, sigma, gas_cost, trade_size);
// Generate independent market price
double market_price = S_t + utils::normal_dist(0.0, sigma);
// Determine if trades should occur based on market price and spreads
bool is_buy = (market_price = adjusted_delta_a);
// Execute trades and update inventory
if (is_buy) {
inventory_.update_inventory(trade_size, true);
std::cout reset();
// Take action and get new state, reward, and done flag
std::tuple, double, bool> step(const std::array& action);
private:
// Get current environment state
std::vector get_state() const;
MarketMaker& mm_;
double current_inventory_;
double current_profit_;
int current_step_;
int max_steps_;
// Current market parameters
double mid_price_;
double sigma_;
double latency_;
double pool_depth_;
std::mt19937 rng_;
};
Trạng thái môi trường của chúng ta là một vector gồm giá hiện tại, tồn kho, độ biến động, độ trễ mạng và độ sâu pool. Hành động là một vector gồm spread và kích thước mua/bán.
Bây giờ hãy triển khai hàm phần thưởng:
double reward = profit_term - inventory_risk - gas_cost;
Trong đó:
profit_termlà lợi nhuận từ các giao dịchinventory_risklà hình phạt cho tồn kho lớn (rủi ro)gas_costlà gas đã tiêu thụ
Cuối cùng, hãy huấn luyện agent PPO:
void PPOTrainer::train(int episodes) {
for (int ep = 0; ep states;
std::vector actions;
std::vector rewards;
while (true) {
// Get action from policy
auto action_probs = policy_net_->forward(torch::tensor(state));
auto action = action_probs.multinomial(1);
// Take a step in the environment
auto [next_state, reward, done] = env_.step(action);
// Save transition
states.push_back(torch::tensor(state));
actions.push_back(action);
rewards.push_back(reward);
if (done) break;
state = next_state;
}
// Update PPO policy
update_policy(states, actions, rewards);
}
}
Học Tăng Cường trong hành động: Agent PPO xử lý các trạng thái thị trường phức tạp để tối ưu hóa động các spread mua/bán nhằm đạt phần thưởng kỳ vọng tối đa.
Bước 7: Kiểm Thử Và Trực Quan Hóa
Để kiểm thử thuật toán của chúng ta, hãy tạo một mô phỏng đơn giản:
int main() {
// Use T = 300 seconds as specified in the task
MarketMaker mm(0.1, 300.0);
// Simulate historical data for volatility
std::vector prices = {2000.0};
double S_t = 2000.0;
double trade_size = 1.0;
double initial_sigma = 0.05; // 5% volatility
for (int i = 0; i < 300; ++i) {
std::cout << "Step " << i + 1 << ": ";
// Get data (stubs)
auto [mid_price, bid_ask] = mm.get_binance_data("USD+/wETH");
auto [gas_cost, latency] = mm.get_onchain_metrics();
// Add random price movement to simulate a real market
S_t = mid_price + utils::normal_dist(0.0, mid_price * 0.01);
// Calculate volatility
double sigma = mm.calculate_volatility(prices, 5);
if (sigma < 0.01) sigma = initial_sigma;
// Order arrival rate (stub)
double k = 5.0;
mm.step(S_t, sigma, k, latency, gas_cost, trade_size);
// Update price for next step
S_t += utils::normal_dist(0.0, S_t * 0.02);
prices.push_back(S_t);
}
return 0;
}
Tiếp Theo Là Gì?
Thuật toán tạo lập thị trường của chúng ta đã sẵn sàng, nhưng có nhiều cách để cải thiện nó:
- Kết nối với API thực: thay thế stub bằng các yêu cầu thực đến Binance API và Ethereum node
- Cải thiện mô hình biến động: sử dụng GARCH hoặc các mô hình nâng cao khác
- Mở rộng PPO: thêm nhiều tham số hơn vào trạng thái và hành động
- Tối ưu hóa gas: các chiến lược để giảm thiểu chi phí gas
- Chiến lược đa tài sản: mở rộng sang nhiều cặp cùng một lúc
Kết Luận
Chúng ta đã xây dựng một thuật toán tạo lập thị trường tính đến các tính năng giao dịch onchain và sử dụng cả mô hình A-S cổ điển lẫn các phương pháp RL hiện đại. Phương pháp này cho phép bạn thích nghi với các điều kiện thị trường thay đổi và tối đa hóa lợi nhuận trong khi kiểm soát rủi ro.
Tất nhiên, trong giao dịch thực tế có nhiều yếu tố bổ sung cần xem xét, nhưng thuật toán của chúng ta cung cấp một nền tảng vững chắc để phát triển thêm. Hãy nhớ: trong giao dịch thuật toán, không chỉ toán học mà còn cả việc kiểm thử kỹ lưỡng, giám sát và tối ưu hóa liên tục đều quan trọng.
Tôi hy vọng bài viết này giúp bạn hiểu rõ hơn các nguyên tắc tạo lập thị trường và truyền cảm hứng để bạn tạo ra các thuật toán của riêng mình. Chúc bạn giao dịch thành công!
Trích Dẫn
@software{soloviov2025marketmakingavellanedastoikov,
author = {Soloviov, Eugen},
title = {Building a Market Making Algorithm for Crypto Pairs Using the Avellaneda-Stoikov Model},
year = {2025},
url = {https://marketmaker.cc/vi/blog/post/market-making-avellaneda-stoikov},
version = {0.1.0},
description = {Hướng dẫn từng bước xây dựng thuật toán tạo lập thị trường cho các cặp USD+/wETH và USD+/cbbtc sử dụng mô hình Avellaneda-Stoikov và PPO. Các tính năng giao dịch onchain, quản lý tồn kho, huấn luyện RL.}
}
Tác Giả
Trading-systems engineer
Trading-systems engineer building bots since 2017: cross-exchange arbitrage (connected up to 30 venues), cointegration-based pairs arbitrage across spot and futures, scalping, news and sentiment-driven strategies, trend algorithms, and portfolio management and balancing algorithms. Also builds sub-millisecond order execution, big-data warehouses, backtesting engines, AI agents, and trading interfaces (incl. open-source profitmaker.cc). Stack: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, architecture.