← 返回文章列表
May 17, 2025
5 分鐘閱讀

基於Avellaneda-Stoikov模型的加密貨幣對做市商演算法構建指南

#做市商
#加密貨幣
#Avellaneda-Stoikov
#演算法交易
#強化學習
#PPO
#DeFi

大家好!今天我將帶你一步步構建一個適用於USD+/wETH和USD+/cbbtc等加密貨幣對的做市商演算法。我們將採用Avellaneda-Stoikov(A-S)模型,並結合強化學習(PPO)演算法實現動態價差最佳化。聽起來很複雜?別擔心,我會拆解成易懂的步驟,即使是初學者也能上手。

演算法做市視覺化 做市本質的視覺化:圍繞公允價格不斷平衡買入和賣出訂單,以合成最優的市場流動性。

什麼是做市商?為什麼需要做市商?

做市商是一種策略,交易者同時掛出買單和賣單,通過買賣價差(spread)獲利。在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 時間視窗

鏈上交易的特殊性

將演算法遷移到鏈上,會遇到額外挑戰:

  1. 延遲——區塊鏈交易非即時,訂單成交前價格可能已變
  2. Gas費——每筆交易都要支付網路手續費
  3. AMM/PMM機制——流動性池機制與傳統交易所有所不同

下面看看如何在演算法中考慮這些因素。

步驟1:環境搭建與資料採集

首先需要搭建環境以獲取市場資料。我們用Binance API獲取當前價格和訂單簿深度。

std::tuple MarketMaker::get_binance_data(const std::string& pair) {
    // 实际代码中这里会请求Binance API
    // 返回: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};
}

還需要鏈上指標,如gas費和網路延遲:

std::pair MarketMaker::get_onchain_metrics() {
    // 实际代码中这里会请求以太坊节点
    // 返回:gas_price (wei), latency (秒)
    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公式
    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;  // 卖出价
    double delta_b = S_t - spread_term - inventory_term;  // 买入价
    
    return {delta_a, delta_b};
}

注意inventory_term。如果你庫存為正(手裡有很多幣),賣出價會降低,買入價會更低,以鼓勵賣出、抑制買入。反之亦然。

步驟3:適配鏈上交易特性

現在要考慮區塊鏈的特殊性。先看延遲:

double MarketMaker::adjust_price_with_latency(double S_t, double sigma, double latency) {
    // 用延迟模拟价格的随机变化
    double latency_adjustment = utils::normal_dist(0.0, sigma * std::sqrt(latency));
    return S_t + latency_adjustment;
}

這裡用的是隨機遊走模型:波動率越大、延遲越長,訂單成交前價格變化越大。

再看gas費:

double MarketMaker::calculate_gas_cost(double gas_price, double trade_size) {
    const double GAS_LIMIT_PER_ORDER = 100000;  // 每单大致gas消耗
    return (gas_price * GAS_LIMIT_PER_ORDER * trade_size) / 1e18;  // wei转ETH
}

最後,適配PMM池的價差:

std::pair MarketMaker::adjust_spreads_for_pmm(double S_t, double delta_a, double delta_b, double pool_depth) {
    // 简化PMM模型:根据池深调整价差
    const double MIN_POOL_DEPTH = 10.0;
    double depth_factor = std::max(pool_depth, MIN_POOL_DEPTH) / MIN_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) {
    // 获取当前库存
    double current_inventory = inventory_.get_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);
    
    // 生成独立市场价格
    double market_price = S_t + utils::normal_dist(0.0, sigma);
    
    // 判断是否成交
    bool is_buy = (market_price = adjusted_delta_a);
    
    // 执行交易并更新库存
    if (is_buy) {
        inventory_.update_inventory(trade_size, true);
        std::cout  reset();
    
    // 执行动作,获取新状态、奖励和done标志
    std::tuple, double, bool> step(const std::array& action);
    
private:
    // 获取当前环境状态
    std::vector get_state() const;
    
    MarketMaker& mm_;
    double current_inventory_;
    double current_profit_;
    int current_step_;
    int max_steps_;
    
    // 当前市场参数
    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 gas消耗

最後,訓練PPO智慧體:

void PPOTrainer::train(int episodes) {
    for (int ep = 0; ep  states;
        std::vector actions;
        std::vector rewards;
        
        while (true) {
            // 策略网络输出动作
            auto action_probs = policy_net_->forward(torch::tensor(state));
            auto action = action_probs.multinomial(1);
            
            // 环境执行一步
            auto [next_state, reward, done] = env_.step(action);
            
            // 保存转移
            states.push_back(torch::tensor(state));
            actions.push_back(action);
            rewards.push_back(reward);
            
            if (done) break;
            state = next_state;
        }
        
        // PPO策略更新
        update_policy(states, actions, rewards);
    }
}

PPO強化學習智慧體用於價差最佳化 執行中的強化學習:PPO智慧體處理複雜的市場狀態,從而動態最佳化買賣價差,實現預期收益最大化

步驟7:測試與視覺化

為了測試演算法,建立一個簡單的模擬:

int main() {
    // 任务要求T=300秒
    MarketMaker mm(0.1, 300.0);

    // 波动率历史数据仿真
    std::vector prices = {2000.0};
    double S_t = 2000.0;
    double trade_size = 1.0;
    double initial_sigma = 0.05;  // 5%波动率

    for (int i = 0; i < 300; ++i) {
        std::cout << "Step " << i + 1 << ": ";

        // 获取数据(模拟)
        auto [mid_price, bid_ask] = mm.get_binance_data("USD+/wETH");
        auto [gas_cost, latency] = mm.get_onchain_metrics();
        
        // 随机价格波动,模拟真实市场
        S_t = mid_price + utils::normal_dist(0.0, mid_price * 0.01);

        // 计算波动率
        double sigma = mm.calculate_volatility(prices, 5);
        if (sigma < 0.01) sigma = initial_sigma;

        // 订单到达率(模拟)
        double k = 5.0;

        mm.step(S_t, sigma, k, latency, gas_cost, trade_size);

        // 更新下一步价格
        S_t += utils::normal_dist(0.0, S_t * 0.02);
        prices.push_back(S_t);
    }

    return 0;
}

後續展望

演算法已完成,但還有很多最佳化空間:

  1. 接入真實API:用真實Binance API和以太坊節點替換模擬
  2. 改進波動率模型:用GARCH等高階模型
  3. 擴充PPO:增加更多狀態和動作參數
  4. 最佳化gas:最小化gas消耗的策略
  5. 多幣種策略:擴充到多交易對

結論

我們構建了一個兼顧鏈上特性、結合A-S模型與現代RL方法的做市商演算法。這種方法能適應市場變化,在控制風險的同時最大化收益。

當然,真實交易中還有許多額外因素需考慮,但本演算法為後續開發打下了堅實基礎。請記住:演算法交易不僅要有數學,更要有充分測試、監控和持續最佳化。

希望本文能幫助你理解做市商原理,並激勵你開發自己的演算法。祝交易順利!

引文

@software{soloviov2025marketmakingavellanedastoikov,
  author = {Soloviov, Eugen},
  title = {基于Avellaneda-Stoikov模型的加密货币对做市商算法构建指南},
  year = {2025},
  url = {https://marketmaker.cc/zh/blog/post/market-making-avellaneda-stoikov},
  version = {0.1.0},
  description = {一步步教你如何用Avellaneda-Stoikov模型和PPO算法为USD+/wETH和USD+/cbbtc等加密货币对构建做市商算法。涵盖链上交易特性、库存管理、强化学习训练等。}
}
免責宣告:本文提供的資訊僅用於教育和參考目的,不構成財務、投資或交易建議。加密貨幣交易涉及重大損失風險。

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

緊跟市場步伐

訂閱我們的時事通訊,獲取獨家 AI 交易見解、市場分析和平台更新。

我們尊重您的隱私。您可以隨時退訂。