การสร้างอัลกอริทึม Market Making สำหรับคู่เหรียญคริปโตด้วยโมเดล Avellaneda-Stoikov
สวัสดีเพื่อนๆ ทุกคน! วันนี้ผมจะแสดงวิธีการสร้างอัลกอริทึม market making สำหรับคู่เหรียญ USD+/wETH และ USD+/cbbtc เราจะใช้โมเดล Avellaneda-Stoikov (A-S) และเพิ่มประสิทธิภาพด้วยอัลกอริทึม Reinforcement Learning (PPO) สำหรับการปรับ spread แบบไดนามิก ฟังดูซับซ้อนไหม? ไม่ต้องกังวล ผมจะอธิบายทุกอย่างเป็นขั้นตอนที่ชัดเจนเพื่อให้แม้แต่นักพัฒนามือใหม่ก็สามารถทำตามได้
การแสดงภาพแก่นแท้ของ market making: การจัดสมดุลคำสั่งซื้อและขายอย่างต่อเนื่องรอบราคาที่เป็นธรรมเพื่อสังเคราะห์สภาพคล่องของตลาดที่ดีที่สุด
Market Making คืออะไร และทำไมเราจึงต้องการมัน?
Market making เป็นกลยุทธ์ที่เทรดเดอร์วางคำสั่งซื้อและขายสินทรัพย์พร้อมกัน โดยทำกำไรจาก spread (ส่วนต่างระหว่างราคา) ในระบบนิเวศ DeFi, market makers มีบทบาทสำคัญในการจัดหาสภาพคล่องและลด slippage สำหรับผู้ร่วมตลาดคนอื่น
ลองนึกภาพว่าคุณเป็นพ่อค้าในตลาด พร้อมซื้อสินค้าในราคาต่ำกว่าราคาตลาดเล็กน้อยและขายในราคาสูงกว่าเล็กน้อย กำไรของคุณคือส่วนต่างระหว่างราคาซื้อและราคาขาย แต่มีข้อระวัง: หากราคาเคลื่อนที่ไปในทิศทางเดียวอย่างฉับพลัน คุณอาจสะสม inventory มากเกินไป หรือในทางกลับกัน ไม่มีสินค้าเหลือขาย
โมเดล Avellaneda-Stoikov: คณิตศาสตร์ในการรับใช้การเทรด
โมเดล A-S เป็นวิธีการทางคณิตศาสตร์สำหรับการกำหนดราคาที่เหมาะสมสำหรับ market making ข้อดีหลักคือมันไม่เพียงแค่คำนึงถึงราคาตลาดปัจจุบัน แต่ยังรวมถึงขนาดตำแหน่ง (inventory), ความผันผวนของตลาด และความเสี่ยงที่ยอมรับได้
สูตรหลักของโมเดล:
δ_a = S_t + (1/γ) * ln(1 + γ/k) + q_t * σ² * T
δ_b = S_t - (1/γ) * ln(1 + γ/k) - q_t * σ² * T
โดยที่:
δ_aและδ_bคือราคา ask และ bidS_tคือราคาตลาดปัจจุบันγคือพารามิเตอร์ความเสี่ยง (ยิ่งสูง spread ยิ่งกว้าง)kคืออัตราการมาถึงของคำสั่งq_tคือ inventory ปัจจุบันσคือความผันผวนTคือขอบเวลา
ฟีเจอร์การเทรด Onchain
เมื่อเราย้ายอัลกอริทึมไป onchain จะเกิดความท้าทายเพิ่มเติม:
- Latency – ธุรกรรมบน blockchain ไม่ได้เกิดขึ้นทันที และราคาอาจเปลี่ยนแปลงก่อนที่คำสั่งจะถูกดำเนินการ
- Gas costs – ทุกธุรกรรมต้องใช้ค่าธรรมเนียมเครือข่าย
- AMM/PMM specifics – กลไกของ liquidity pool แตกต่างจากตลาดแบบดั้งเดิม
มาดูวิธีการคำนึงถึงปัจจัยเหล่านี้ในอัลกอริทึมของเรา
ขั้นตอนที่ 1: ตั้งค่าสภาพแวดล้อมและเก็บข้อมูล
ก่อนอื่น เราต้องตั้งค่าสภาพแวดล้อมเพื่อรับข้อมูลตลาด เราจะใช้ Binance API เพื่อรับราคาปัจจุบันและความลึกของ 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};
}
เราจะต้องใช้ onchain metrics เช่น gas cost และ network latency ด้วย:
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};
}
ขั้นตอนที่ 2: ใช้งานโมเดล A-S พื้นฐาน
ตอนนี้มาใช้งานการคำนวณ spread โดยใช้โมเดล 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};
}
สังเกต inventory_term หากคุณมี inventory บวก (มีสินทรัพย์มาก) ราคา ask จะลดลง และราคา bid ลดลงมากกว่าเพื่อกระตุ้นการขายและจำกัดการซื้อ และในทางกลับกันสำหรับ inventory ติดลบ
ขั้นตอนที่ 3: ปรับโมเดลสำหรับการเทรด Onchain
ตอนนี้เราต้องคำนึงถึงข้อจำเพาะของ blockchain มาเริ่มด้วย latency:
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;
}
ที่นี่เราใช้โมเดล random walk: ยิ่งความผันผวนสูงและ latency นานขึ้น ราคายิ่งเปลี่ยนแปลงได้มากก่อนที่คำสั่งจะถูกดำเนินการ
ตอนนี้มาคำนึงถึง gas cost:
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
}
สุดท้าย มาปรับ spread สำหรับข้อจำเพาะของ PMM pool:
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};
}
ขั้นตอนที่ 4: การจัดการ Inventory
เพื่อติดตามและจัดการ inventory มาสร้าง class ง่ายๆ:
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_;
};
การแสดงภาพ Inventory Risk: การติดตามขนาดตำแหน่งเพื่อหลีกเลี่ยงการเปิดรับความเสี่ยงมากเกินไป (long หรือ short) ต่อการเคลื่อนไหวของตลาดในทิศทางเดียว
ขั้นตอนที่ 5: รวมทุกอย่างเป็นอัลกอริทึมเดียว
ตอนนี้มารวมองค์ประกอบทั้งหมดเป็นอัลกอริทึม market making เดียว:
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_;
};
สถานะสภาพแวดล้อมของเราคือ vector ของราคาปัจจุบัน, inventory, ความผันผวน, network latency และ pool depth การกระทำคือ vector ของ spread และขนาดซื้อ/ขาย
ตอนนี้มาใช้งาน reward function:
double reward = profit_term - inventory_risk - gas_cost;
โดยที่:
profit_termคือกำไรจากการเทรดinventory_riskคือบทลงโทษสำหรับ inventory ขนาดใหญ่ (ความเสี่ยง)gas_costคือ gas ที่ใช้ไป
สุดท้าย มาฝึก PPO agent:
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);
}
}
Reinforcement Learning ในการทำงาน: PPO agent ประมวลผลสถานะตลาดที่ซับซ้อนเพื่อปรับ spread ซื้อ/ขายแบบไดนามิกเพื่อรางวัลที่คาดหวังสูงสุด
ขั้นตอนที่ 7: การทดสอบและการแสดงภาพ
เพื่อทดสอบอัลกอริทึมของเรา มาสร้างการจำลองง่ายๆ:
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;
}
ขั้นตอนต่อไปคืออะไร?
อัลกอริทึม market making ของเราพร้อมแล้ว แต่มีหลายวิธีในการปรับปรุง:
- เชื่อมต่อกับ API จริง: แทนที่ stub ด้วยคำขอจริงไปยัง Binance API และ Ethereum node
- ปรับปรุงโมเดลความผันผวน: ใช้ GARCH หรือโมเดลขั้นสูงอื่นๆ
- ขยาย PPO: เพิ่มพารามิเตอร์มากขึ้นสำหรับ state และ action
- ปรับให้ gas ดีที่สุด: กลยุทธ์เพื่อลด gas costs ให้เหลือน้อยที่สุด
- กลยุทธ์หลายสินทรัพย์: ขยายไปยังหลายคู่พร้อมกัน
บทสรุป
เราได้สร้างอัลกอริทึม market making ที่คำนึงถึงฟีเจอร์การเทรด onchain และใช้ทั้งโมเดล A-S แบบคลาสสิกและวิธีการ RL สมัยใหม่ แนวทางนี้ช่วยให้คุณปรับตัวกับสภาวะตลาดที่เปลี่ยนแปลงและเพิ่มกำไรสูงสุดในขณะที่ควบคุมความเสี่ยง
แน่นอนในการเทรดจริงมีปัจจัยเพิ่มเติมอีกมากมายที่ต้องคำนึงถึง แต่อัลกอริทึมของเราให้รากฐานที่มั่นคงสำหรับการพัฒนาต่อไป จำไว้ว่า: ในการเทรดแบบอัลกอริทึม ไม่ใช่แค่คณิตศาสตร์เท่านั้นที่สำคัญ แต่ยังรวมถึงการทดสอบอย่างละเอียด, การติดตาม และการปรับให้ดีที่สุดอย่างต่อเนื่อง
ผมหวังว่าบทความนี้จะช่วยให้คุณเข้าใจหลักการของ market making ได้ดีขึ้น และสร้างแรงบันดาลใจให้คุณสร้างอัลกอริทึมของตัวเอง ขอให้โชคดีในการเทรด!
การอ้างอิง
@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/th/blog/post/market-making-avellaneda-stoikov},
version = {0.1.0},
description = {คู่มือทีละขั้นตอนสำหรับการสร้างอัลกอริทึม market making สำหรับคู่ USD+/wETH และ USD+/cbbtc โดยใช้โมเดล Avellaneda-Stoikov และ PPO รวมถึงฟีเจอร์การเทรด onchain, การจัดการ inventory และการฝึก RL}
}
ผู้เขียน
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.