Avellaneda-Stoikov modelidan foydalanib kripto juftliklar uchun market-meyking algoritmini yaratish
Salom, do'stlar! Bugun men sizlarga USD+/wETH va USD+/cbbtc kripto juftliklari uchun market-meyking algoritmini qanday yaratishni ko'rsataman. Biz Avellaneda-Stoikov (A-S) modelidan foydalanamiz va uni spredni dinamik optimallashtirish uchun Reinforcement Learning (PPO) algoritmi bilan yaxshilaymiz. Murakkab tuyuladimi? Xavotir olmang, men hammasini aniq qadamlarga bo'lib beraman, shunda hatto yangi boshlagan dasturchi ham tushuna oladi.
Market-meykingning mohiyatini vizuallashtirish: optimal bozor likvidligini sintez qilish uchun adolatli narx atrofida sotib olish va sotish buyurtmalarini uzluksiz muvozanatlash.
Market-meyking nima va u bizga nima uchun kerak?
Market-meyking — bu treyder aktiv uchun bir vaqtning o'zida sotib olish va sotish buyurtmalarini joylashtiradigan, spreddan (narxlar orasidagi farqdan) daromad oladigan strategiya. DeFi sohasida market-meykerlar likvidlik ta'minlash va boshqa bozor ishtirokchilari uchun slippajni kamaytirish orqali muhim rol o'ynaydi.
Tasavvur qiling, siz bozordagi sotuvchisiz, doimo tovarni bozor narxidan biroz pastroqqa sotib olishga va biroz yuqoriroqqa sotishga tayyor. Sizning foydangiz sotib olish va sotish narxlaringiz orasidagi farqdir. Ammo bir nuqta bor: agar narx birdaniga bir yo'nalishda harakatlansa, sizda juda ko'p inventar to'planib qolishi mumkin yoki, aksincha, sotish uchun hech narsa qolmasligi mumkin.
Avellaneda-Stoikov modeli: treyding xizmatidagi matematika
A-S modeli market-meyking uchun optimal narxlarni aniqlashning matematik yondashuvidir. Uning asosiy afzalligi shundaki, u nafaqat joriy bozor narxini, balki sizning pozitsiya hajmingizni (inventar), bozor volatilligini va tavakkalchilikka moyillikni ham hisobga oladi.
Modelning asosiy formulalari:
δ_a = S_t + (1/γ) * ln(1 + γ/k) + q_t * σ² * T
δ_b = S_t - (1/γ) * ln(1 + γ/k) - q_t * σ² * T
bu yerda:
δ_avaδ_b— ask va bid narxlariS_t— joriy bozor narxiγ— tavakkal parametri (qanchalik yuqori bo'lsa, spred shunchalik keng bo'ladi)k— buyurtmalar kelish tezligiq_t— joriy inventarσ— volatillikT— vaqt gorizonti
Onchain treydingning xususiyatlari
Algoritmni onchainga o'tkazganimizda qo'shimcha qiyinchiliklar paydo bo'ladi:
- Kechikish (latency) – blokcheyndagi tranzaksiyalar bir zumda amalga oshmaydi, shuning uchun buyurtma bajarilgunga qadar narx o'zgarishi mumkin
- Gaz xarajatlari – har bir tranzaksiya uchun tarmoq komissiyasi talab qilinadi
- AMM/PMM xususiyatlari – likvidlik pullarining mexanikasi an'anaviy birjalardan farq qiladi
Keling, bu omillarni algoritmimizda qanday hisobga olishimizni ko'rib chiqaylik.
1-qadam: Muhitni sozlash va ma'lumotlarni yig'ish
Avvalo, bozor ma'lumotlarini olish uchun muhitni sozlashimiz kerak. Joriy narxlar va buyurtmalar kitobining chuqurligini olish uchun Binance API'dan foydalanamiz.
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};
}
Bizga gaz xarajati va tarmoq kechikishi kabi onchain metrikalari ham kerak bo'ladi:
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-qadam: Asosiy A-S modelini amalga oshirish
Endi A-S modelidan foydalanib spred hisobini amalga oshiramiz:
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_termga e'tibor bering. Agar sizda musbat inventar bo'lsa (aktivning ko'p miqdori), ask narxi pasayadi, bid narxi esa yanada ko'proq pasayadi, bu sotishni rag'batlantiradi va sotib olishni cheklaydi. Manfiy inventar uchun esa aksincha.
3-qadam: Modelni onchain treyding uchun moslashtirish
Endi blokcheyn xususiyatlarini hisobga olishimiz kerak. Kechikishdan boshlaylik:
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;
}
Bu yerda biz tasodifiy yurish (random walk) modelidan foydalanamiz: volatillik qanchalik yuqori va kechikish qanchalik uzoq bo'lsa, buyurtma bajarilgunga qadar narx shunchalik ko'proq o'zgarishi mumkin.
Endi gaz xarajatini hisobga olaylik:
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
}
Nihoyat, spredlarni PMM pul xususiyatlariga moslashtiraylik:
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-qadam: Inventarni boshqarish
Inventarni kuzatish va boshqarish uchun oddiy klass yaratamiz:
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_;
};
Inventar tavakkalini vizuallashtirish: bir yo'nalishli bozor harakatlariga haddan tashqari ta'sirlanishning (long yoki short) oldini olish uchun pozitsiya hajmlarini kuzatish.
5-qadam: Hammasini yagona algoritmga birlashtirish
Endi barcha komponentlarni yagona market-meyking algoritmiga birlashtiramiz:
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_;
};
Muhitimizning holati joriy narx, inventar, volatillik, tarmoq kechikishi va pul chuqurligidan iborat vektordir. Amal esa spredlar va sotib olish/sotish hajmlaridan iborat vektordir.
Endi mukofot funksiyasini amalga oshiramiz:
double reward = profit_term - inventory_risk - gas_cost;
Bu yerda:
profit_term— savdolardan olingan foydainventory_risk— katta inventar uchun jarima (tavakkal)gas_cost— sarflangan gaz
Nihoyat, PPO agentini o'qitamiz:
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);
}
}
Amaldagi Reinforcement Learning: PPO agenti maksimal kutilayotgan mukofot uchun sotib olish/sotish spredlarini dinamik ravishda optimallashtirish maqsadida murakkab bozor holatlarini qayta ishlaydi.
7-qadam: Sinov va vizuallashtirish
Algoritmimizni sinash uchun oddiy simulyatsiya yaratamiz:
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;
}
Keyin nima?
Market-meyking algoritmimiz tayyor, ammo uni yaxshilashning ko'plab yo'llari bor:
- Haqiqiy API'larga ulanish: stublarni Binance API va Ethereum tuguniga haqiqiy so'rovlar bilan almashtirish
- Volatillik modelini yaxshilash: GARCH yoki boshqa ilg'or modellardan foydalanish
- PPOni kengaytirish: holat va amalga ko'proq parametr qo'shish
- Gazni optimallashtirish: gaz xarajatlarini minimallashtirish strategiyalari
- Ko'p aktivli strategiya: bir vaqtning o'zida bir nechta juftlikka kengaytirish
Xulosa
Biz onchain treydingning xususiyatlarini hisobga oladigan hamda klassik A-S modelidan ham, zamonaviy RL usullaridan ham foydalanadigan market-meyking algoritmini yaratdik. Bu yondashuv o'zgaruvchan bozor sharoitlariga moslashish va tavakkalni nazorat qilgan holda foydani maksimallashtirish imkonini beradi.
Albatta, haqiqiy treydingda hisobga olinishi kerak bo'lgan ko'plab qo'shimcha omillar mavjud, ammo algoritmimiz keyingi rivojlanish uchun mustahkam asos beradi. Esda tuting: algoritmik treydingda faqat matematika emas, balki puxta sinov, monitoring va doimiy optimallashtirish ham muhim.
Ushbu maqola sizga market-meyking tamoyillarini yaxshiroq tushunishga yordam berdi va o'z algoritmlaringizni yaratishga ilhomlantirdi, degan umiddaman. Treydingda omad tilayman!
Iqtibos
@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/en/blog/post/market-making-avellaneda-stoikov},
version = {0.1.0},
description = {A step-by-step guide to building a market making algorithm for USD+/wETH and USD+/cbbtc pairs using the Avellaneda-Stoikov model and PPO. Onchain trading features, inventory management, RL training.}
}
Authors
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.