Avellaneda-Stoikov моделін пайдаланып крипто жұптарына арналған маркет-мейкинг алгоритмін құру
Сәлем, достар! Бүгін мен сендерге USD+/wETH және USD+/cbbtc крипто жұптарына арналған маркет-мейкинг алгоритмін қалай құру керегін көрсетемін. Біз Avellaneda-Stoikov (A-S) моделін қолданамыз және оны спредті динамикалық оңтайландыру үшін Reinforcement Learning (PPO) алгоритмімен жетілдіреміз. Күрделі болып көріне ме? Уайымдамаңыз, мен бәрін нақты қадамдарға бөлемін, сондықтан бастаушы әзірлеуші де түсіне алады.
Маркет-мейкингтің мәнін бейнелеу: оңтайлы нарықтық өтімділікті синтездеу үшін әділ баға айналасында сатып алу және сату ордерлерін үздіксіз теңгеру.
Маркет-мейкинг деген не және ол бізге неге қажет?
Маркет-мейкинг — бұл трейдер активке бір мезгілде сатып алу және сату ордерлерін орналастырып, спредтен (бағалар арасындағы айырмашылықтан) табыс табатын стратегия. DeFi саласында маркет-мейкерлер өтімділік қамтамасыз ету және басқа нарық қатысушылары үшін слиппажды азайту арқылы негізгі рөл атқарады.
Сендер базардағы саудагер деп елестетіңдер, әрдайым тауарды нарықтық бағадан сәл төмен сатып алуға және сәл жоғары сатуға дайын. Сендердің пайдаларың сатып алу мен сату бағаларының арасындағы айырмашылық. Бірақ бір қиындық бар: егер баға кенеттен бір бағытта қозғалса, сендерде тым көп инвентарь жиналуы мүмкін немесе, керісінше, сатуға ештеңе қалмауы мүмкін.
Avellaneda-Stoikov моделі: трейдинг қызметіндегі математика
A-S моделі маркет-мейкингке арналған оңтайлы бағаларды анықтаудың математикалық тәсілі болып табылады. Оның басты артықшылығы — тек ағымдағы нарықтық бағаны ғана емес, сонымен қатар сендердің позиция көлеміңді (инвентарь), нарық құбылмалылығын және тәуекелге деген құштарлықты да ескереді.
Модельдің негізгі формулалары:
δ_a = S_t + (1/γ) * ln(1 + γ/k) + q_t * σ² * T
δ_b = S_t - (1/γ) * ln(1 + γ/k) - q_t * σ² * T
мұндағы:
δ_aжәнеδ_b— аск және бид бағаларыS_t— ағымдағы нарықтық бағаγ— тәуекел параметрі (неғұрлым жоғары болса, спред соғұрлым кең болады)k— ордерлердің келу жиілігіq_t— ағымдағы инвентарьσ— құбылмалылықT— уақыт горизонты
Ончейн трейдингтің ерекшеліктері
Алгоритмді ончейнге көшіргенде қосымша қиындықтар туындайды:
- Кідіріс (latency) – блокчейндегі транзакциялар лезде жүрмейді, сондықтан ордер орындалғанға дейін баға өзгеруі мүмкін
- Газ шығындары – әрбір транзакция үшін желі комиссиясы қажет
- AMM/PMM ерекшеліктері – өтімділік пулдарының механикасы дәстүрлі биржалардан ерекшеленеді
Осы факторларды алгоритмімізде қалай ескеретінімізді көрейік.
1-қадам: Ортаны баптау және деректерді жинау
Алдымен нарықтық деректерді алу үшін ортаны баптауымыз керек. Ағымдағы бағаларды және ордер кітабының тереңдігін алу үшін Binance API қолданамыз.
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};
}
Бізге газ шығыны мен желі кідірісі сияқты ончейн метрикалары да қажет болады:
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 моделін іске асыру
Енді 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-ге назар аударыңдар. Егер сендерде оң инвентарь болса (активтің көп мөлшері), аск бағасы төмендейді, ал бид бағасы одан да көбірек төмендейді, бұл сатуды ынталандырады және сатып алуды шектейді. Теріс инвентарь үшін керісінше.
3-қадам: Модельді ончейн трейдингке бейімдеу
Енді блокчейн ерекшеліктерін ескеруіміз керек. Кідірістен бастайық:
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) моделін қолданамыз: құбылмалылық неғұрлым жоғары және кідіріс неғұрлым ұзақ болса, ордер орындалғанға дейін баға соғұрлым көп өзгере алады.
Енді газ шығынын ескерейік:
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
}
Соңында, спредтерді 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};
}
4-қадам: Инвентарды басқару
Инвентарды бақылау және басқару үшін қарапайым класс жасайық:
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_;
};
Инвентарь тәуекелін визуализациялау: бірбағытты нарықтық қозғалыстарға шамадан тыс ұшырауды (лонг немесе шорт) болдырмау үшін позиция көлемдерін бақылау.
5-қадам: Барлығын бір алгоритмге біріктіру
Енді барлық компоненттерді бір маркет-мейкинг алгоритміне біріктірейік:
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_;
};
Ортамыздың күйі — ағымдағы баға, инвентарь, құбылмалылық, желі кідірісі және пул тереңдігінен тұратын вектор. Әрекет — спредтер мен сатып алу/сату көлемдерінің векторы.
Енді сыйақы функциясын іске асырайық:
double reward = profit_term - inventory_risk - gas_cost;
Мұндағы:
profit_term— сауда-саттықтан алынған пайдаinventory_risk— үлкен инвентарь үшін айыппұл (тәуекел)gas_cost— жұмсалған газ
Соңында, 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);
}
}
Іс-әрекеттегі Reinforcement Learning: PPO агенті максималды күтілетін сыйақы үшін сатып алу/сату спредтерін динамикалық түрде оңтайландыру мақсатында күрделі нарық күйлерін өңдейді.
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;
}
Ары қарай не істеу керек?
Маркет-мейкинг алгоритміміз дайын, бірақ оны жетілдірудің көптеген жолдары бар:
- Нақты API-ларға қосылу: заглушкаларды Binance API мен Ethereum түйініне нақты сұраныстармен ауыстыру
- Құбылмалылық моделін жетілдіру: GARCH немесе басқа озық модельдерді пайдалану
- PPO-ны кеңейту: күй мен әрекетке көбірек параметр қосу
- Газды оңтайландыру: газ шығындарын азайту стратегиялары
- Мульти-активті стратегия: бірден бірнеше жұпқа кеңейту
Қорытынды
Біз ончейн трейдингтің ерекшеліктерін ескеретін және классикалық A-S моделін де, заманауи RL әдістерін де пайдаланатын маркет-мейкинг алгоритмін құрдық. Бұл тәсіл өзгермелі нарық жағдайларына бейімделуге және тәуекелді бақылай отырып, пайданы барынша арттыруға мүмкіндік береді.
Әрине, нақты трейдингте ескеруге тиіс көптеген қосымша факторлар бар, бірақ алгоритміміз одан әрі дамыту үшін берік негіз береді. Есте сақтаңдар: алгоритмдік трейдингте тек математика ғана емес, мұқият тестілеу, мониторинг және үздіксіз оңтайландыру да маңызды.
Осы мақала маркет-мейкинг қағидаттарын жақсырақ түсінуге көмектесті және сендерді өз алгоритмдеріңді жасауға шабыттандырды деп үміттенемін. Трейдингте сәттілік тілеймін!
Дәйексөз
@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.