Avellaneda-Stoikov Modelini Kullanarak Kripto Çiftleri için Piyasa Yapıcı Algoritması Oluşturma
Merhaba, arkadaşlar! Bugün USD+/wETH ve USD+/cbbtc kripto çiftleri için nasıl piyasa yapıcı algoritması oluşturacağımızı göstereceğim. Avellaneda-Stoikov (A-S) modelini kullanacağız ve dinamik spread optimizasyonu için Pekiştirmeli Öğrenme (PPO) algoritmasıyla onu geliştireceğiz. Karmaşık mı geliyor? Endişelenme, başlangıç seviyesindeki bir geliştirici bile takip edebilsin diye her şeyi açık adımlara böleceğim.
Piyasa yapıcılığın özünü görselleştirme: Optimal piyasa likiditesi sentezlemek için adil fiyat etrafında sürekli olarak alış ve satış emirlerini dengeleme.
Piyasa Yapıcılık Nedir ve Neden İhtiyacımız Var?
Piyasa yapıcılık, bir yatırımcının bir varlık için eş zamanlı olarak alış ve satış emirleri vererek spread'den (fiyatlar arasındaki fark) kazandığı bir stratejidir. DeFi alanında piyasa yapıcılar, diğer piyasa katılımcıları için likidite sağlayarak ve kayma (slippage) oranını azaltarak kilit bir rol oynar.
Bir pazarda her zaman bir ürünü piyasa fiyatının biraz altında satın almaya ve biraz üzerinde satmaya hazır bir satıcı olduğunuzu hayal edin. Kârınız, alış ve satış fiyatlarınız arasındaki farktır. Ancak bir tehlike var: fiyat aniden bir yöne hareket ederse, çok fazla envanter biriktirebilir veya tam tersine satacak hiçbir şey kalmayabilirsiniz.
Avellaneda-Stoikov Modeli: Ticaretin Hizmetinde Matematik
A-S modeli, piyasa yapıcılık için optimal fiyatları belirlemede matematiksel bir yaklaşımdır. Temel avantajı, yalnızca mevcut piyasa fiyatını değil, aynı zamanda pozisyon büyüklüğünüzü (envanter), piyasa oynaklığını ve risk iştahınızı da dikkate almasıdır.
Modelin temel formülleri:
δ_a = S_t + (1/γ) * ln(1 + γ/k) + q_t * σ² * T
δ_b = S_t - (1/γ) * ln(1 + γ/k) - q_t * σ² * T
burada:
δ_aveδ_bsırasıyla alış (ask) ve satış (bid) fiyatlarıdırS_tmevcut piyasa fiyatıdırγrisk parametresidir (ne kadar yüksekse spread o kadar geniştir)kemir gelme hızıdırq_tmevcut envantedirσoynaklıktırTzaman ufkudur
Zincir İçi Ticaret Özellikleri
Algoritmayı zincir içine taşıdığımızda ek zorluklar ortaya çıkar:
- Gecikme (Latency) – blok zincirindeki işlemler anlık değildir ve emir gerçekleşmeden önce fiyat değişebilir
- Gas maliyetleri – her işlem için ağ ücreti gerekir
- AMM/PMM özellikleri – likidite havuzu mekaniği geleneksel borsalardan farklıdır
Bu faktörleri algoritmamızda nasıl hesaba katabileceğimize bakalım.
Adım 1: Ortamı Kur ve Veri Topla
Öncelikle piyasa verisi almak için bir ortam kurulması gerekiyor. Güncel fiyatları ve emir defteri derinliğini almak için Binance API'sini kullanacağız.
std::tuple MarketMaker::get_binance_data(const std::string& pair) {
// Gerçek kodda bu, Binance API'sine yapılan bir istek olacaktır
// Döndürür: 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};
}
Ayrıca gas maliyeti ve ağ gecikmesi gibi zincir içi metriklere de ihtiyacımız olacak:
std::pair MarketMaker::get_onchain_metrics() {
// Gerçek kodda bu, bir Ethereum düğümüne yapılan istek olacaktır
// Döndürür: gas_price (wei), latency (saniye)
return {50e9, 12.0};
}
Adım 2: Temel A-S Modelini Uygula
Şimdi A-S modelini kullanarak spread hesaplamasını uygulayalım:
std::pair MarketMaker::calculate_spreads(double S_t, double sigma, double k, double q_t) {
// Avellaneda-Stoikov formülü
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; // Alış fiyatı
double delta_b = S_t - spread_term - inventory_term; // Satış fiyatı
return {delta_a, delta_b};
}
inventory_term'e dikkat edin. Pozitif envantere (çok fazla varlık) sahipseniz, alış fiyatı düşer ve satışı teşvik edip alımı sınırlamak için satış fiyatı daha da düşer. Negatif envanter için ise tam tersi geçerlidir.
Adım 3: Modeli Zincir İçi Ticarete Uyarla
Şimdi blok zinciri özelliklerini hesaba katmamız gerekiyor. Gecikmeyle başlayalım:
double MarketMaker::adjust_price_with_latency(double S_t, double sigma, double latency) {
// Gecikmeden kaynaklanan rastgele fiyat değişimini simüle et
double latency_adjustment = utils::normal_dist(0.0, sigma * std::sqrt(latency));
return S_t + latency_adjustment;
}
Burada rastgele yürüyüş modeli kullanıyoruz: oynaklık ne kadar yüksek ve gecikme ne kadar uzunsa, emir gerçekleşmeden önce fiyat o kadar fazla değişebilir.
Şimdi gas maliyetini hesaba katalım:
double MarketMaker::calculate_gas_cost(double gas_price, double trade_size) {
const double GAS_LIMIT_PER_ORDER = 100000; // Emir başına yaklaşık değer
return (gas_price * GAS_LIMIT_PER_ORDER * trade_size) / 1e18; // Wei'yi ETH'ye dönüştür
}
Son olarak, PMM havuz özelliklerine göre spread'leri uyarlayalım:
std::pair MarketMaker::adjust_spreads_for_pmm(double S_t, double delta_a, double delta_b, double pool_depth) {
// Basitleştirilmiş PMM modeli: spread'leri havuz derinliğine göre ayarla
const double MIN_POOL_DEPTH = 10.0;
double depth_factor = std::max(pool_depth, MIN_POOL_DEPTH) / MIN_POOL_DEPTH;
// Daha büyük havuz derinliğiyle spread'leri azalt
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};
}
Adım 4: Envanter Yönetimi
Envanteri takip etmek ve yönetmek için basit bir sınıf oluşturalım:
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_;
};
Envanter Riski Görselleştirmesi: Tek yönlü piyasa hareketlerine aşırı maruz kalmayı (uzun veya kısa) önlemek için pozisyon büyüklüklerinin izlenmesi.
Adım 5: Her Şeyi Tek Bir Algoritmada Birleştir
Şimdi tüm bileşenleri tek bir piyasa yapıcı algoritmasında birleştirelim:
void MarketMaker::step(double S_t, double sigma, double k, double latency, double gas_cost, double trade_size) {
// Mevcut envanteri al
double current_inventory = inventory_.get_inventory();
// Mevcut piyasa koşullarına ve envantere göre spread'leri hesapla
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);
// Bağımsız piyasa fiyatı oluştur
double market_price = S_t + utils::normal_dist(0.0, sigma);
// Piyasa fiyatına ve spread'lere göre işlem yapılıp yapılmayacağını belirle
bool is_buy = (market_price = adjusted_delta_a);
// İşlemleri gerçekleştir ve envanteri güncelle
if (is_buy) {
inventory_.update_inventory(trade_size, true);
std::cout reset();
// Aksiyon al ve yeni durum, ödül ve bitiş bayrağını al
std::tuple, double, bool> step(const std::array& action);
private:
// Mevcut ortam durumunu al
std::vector get_state() const;
MarketMaker& mm_;
double current_inventory_;
double current_profit_;
int current_step_;
int max_steps_;
// Mevcut piyasa parametreleri
double mid_price_;
double sigma_;
double latency_;
double pool_depth_;
std::mt19937 rng_;
};
Ortam durumumuz mevcut fiyat, envanter, oynaklık, ağ gecikmesi ve havuz derinliğinden oluşan bir vektördür. Aksiyon ise spread'ler ve alış/satış boyutlarından oluşan bir vektördür.
Şimdi ödül fonksiyonunu uygulayalım:
double reward = profit_term - inventory_risk - gas_cost;
Burada:
profit_termişlemlerden elde edilen kârdırinventory_riskbüyük envanter için bir cezadır (risk)gas_costharcanan gas miktarıdır
Son olarak PPO ajanını eğitelim:
void PPOTrainer::train(int episodes) {
for (int ep = 0; ep states;
std::vector actions;
std::vector rewards;
while (true) {
// Politikadan aksiyon al
auto action_probs = policy_net_->forward(torch::tensor(state));
auto action = action_probs.multinomial(1);
// Ortamda bir adım at
auto [next_state, reward, done] = env_.step(action);
// Geçişi kaydet
states.push_back(torch::tensor(state));
actions.push_back(action);
rewards.push_back(reward);
if (done) break;
state = next_state;
}
// PPO politikasını güncelle
update_policy(states, actions, rewards);
}
}
Pekiştirmeli Öğrenme pratikte: PPO ajanı maksimum beklenen ödül için alış/satış spread'lerini dinamik olarak optimize etmek amacıyla karmaşık piyasa durumlarını işliyor.
Adım 7: Test ve Görselleştirme
Algoritmamızı test etmek için basit bir simülasyon oluşturalım:
int main() {
// Görevde belirtildiği gibi T = 300 saniye kullan
MarketMaker mm(0.1, 300.0);
// Oynaklık için geçmiş veri simüle et
std::vector prices = {2000.0};
double S_t = 2000.0;
double trade_size = 1.0;
double initial_sigma = 0.05; // %5 oynaklık
for (int i = 0; i < 300; ++i) {
std::cout << "Adım " << i + 1 << ": ";
// Veri al (taslaklar)
auto [mid_price, bid_ask] = mm.get_binance_data("USD+/wETH");
auto [gas_cost, latency] = mm.get_onchain_metrics();
// Gerçek bir piyasayı simüle etmek için rastgele fiyat hareketi ekle
S_t = mid_price + utils::normal_dist(0.0, mid_price * 0.01);
// Oynaklığı hesapla
double sigma = mm.calculate_volatility(prices, 5);
if (sigma < 0.01) sigma = initial_sigma;
// Emir gelme hızı (taslak)
double k = 5.0;
mm.step(S_t, sigma, k, latency, gas_cost, trade_size);
// Bir sonraki adım için fiyatı güncelle
S_t += utils::normal_dist(0.0, S_t * 0.02);
prices.push_back(S_t);
}
return 0;
}
Sırada Ne Var?
Piyasa yapıcı algoritmamız hazır, ancak onu geliştirmenin birçok yolu var:
- Gerçek API'lere bağlan: taslakları Binance API ve Ethereum düğümüne gerçek isteklerle değiştir
- Oynaklık modelini geliştir: GARCH veya diğer gelişmiş modelleri kullan
- PPO'yu genişlet: duruma ve aksiyona daha fazla parametre ekle
- Gas'ı optimize et: gas maliyetlerini en aza indirme stratejileri
- Çok varlıklı strateji: aynı anda birden fazla çifte genişlet
Sonuç
Zincir içi ticaret özelliklerini dikkate alan ve hem klasik A-S modelini hem de modern RL yöntemlerini kullanan bir piyasa yapıcı algoritması inşa ettik. Bu yaklaşım, değişen piyasa koşullarına uyum sağlamanıza ve riski kontrol ederken kârı maksimize etmenize olanak tanır.
Elbette gerçek ticarette dikkate alınması gereken pek çok ek faktör var, ancak algoritmamız daha ileri geliştirme için sağlam bir temel sağlıyor. Unutma: algoritmik ticarette yalnızca matematik değil, kapsamlı test, izleme ve sürekli optimizasyon da önemlidir.
Umarım bu makale, piyasa yapıcılık ilkelerini daha iyi anlamanıza yardımcı olmuş ve kendi algoritmalarınızı oluşturmanız için ilham vermiştir. İyi işlemler!
Atıf
@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/tr/blog/post/market-making-avellaneda-stoikov},
version = {0.1.0},
description = {Avellaneda-Stoikov modeli ve PPO kullanarak USD+/wETH ve USD+/cbbtc çiftleri için piyasa yapıcı algoritması oluşturmaya yönelik adım adım rehber. Zincir içi ticaret özellikleri, envanter yönetimi, RL eğitimi.}
}
Yazarlar
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.