← Maqolalarga qaytish
May 17, 2025
5 daqiqa o'qish

Avellaneda-Stoikov modelidan foydalanib kripto juftliklar uchun market-meyking algoritmini yaratish

#market making
#cryptocurrency
#Avellaneda-Stoikov
#algorithmic trading
#reinforcement learning
#PPO
#DeFi

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 Making Visualization 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:

  • δ_a va δ_b — ask va bid narxlari
  • S_t — joriy bozor narxi
  • γ — tavakkal parametri (qanchalik yuqori bo'lsa, spred shunchalik keng bo'ladi)
  • k — buyurtmalar kelish tezligi
  • q_t — joriy inventar
  • σ — volatillik
  • T — vaqt gorizonti

Onchain treydingning xususiyatlari

Algoritmni onchainga o'tkazganimizda qo'shimcha qiyinchiliklar paydo bo'ladi:

  1. Kechikish (latency) – blokcheyndagi tranzaksiyalar bir zumda amalga oshmaydi, shuning uchun buyurtma bajarilgunga qadar narx o'zgarishi mumkin
  2. Gaz xarajatlari – har bir tranzaksiya uchun tarmoq komissiyasi talab qilinadi
  3. 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_;
};

Inventory management and risk visualization 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 foyda
  • inventory_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);
    }
}

PPO Reinforcement Learning agent for spread optimization 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:

  1. Haqiqiy API'larga ulanish: stublarni Binance API va Ethereum tuguniga haqiqiy so'rovlar bilan almashtirish
  2. Volatillik modelini yaxshilash: GARCH yoki boshqa ilg'or modellardan foydalanish
  3. PPOni kengaytirish: holat va amalga ko'proq parametr qo'shish
  4. Gazni optimallashtirish: gaz xarajatlarini minimallashtirish strategiyalari
  5. 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.}
}
blog.disclaimer

Authors

Eugen Soloviov
Eugen Soloviov

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.

Newsletter

Bozordan bir qadam oldinda bo'ling

Sun'iy intellekt savdo tahlillari, bozor tahlili va platforma yangiliklari uchun bizning xabarnomaga obuna bo'ling.

Biz sizning maxfiyligingizni hurmat qilamiz. Istalgan vaqtda obunadan chiqishingiz mumkin.