Автоматический маркет-мейкинг (AMM)
Автоматический маркет-мейкинг (AMM) - это алгоритмический протокол, который автоматически обеспечивает ликвидность на децентрализованных биржах (DEX) без традиционных маркет-мейкеров. Вместо ордербуков AMM используют математические формулы для определения цены и выполнения сделок.
Основные концепции
1. Константный продукт (Constant Product Formula)
Наиболее популярная модель, используемая в Uniswap:
где:
x- количество токена A в пулеy- количество токена B в пулеk- константа продукта
2. Ценообразование
Цена определяется соотношением резервов в пуле:
Реализация базового AMM на Python
import math
from decimal import Decimal
class ConstantProductAMM:
def __init__(self, token_a: str, token_b: str, initial_a: float, initial_b: float):
self.token_a = token_a
self.token_b = token_b
self.reserve_a = Decimal(str(initial_a))
self.reserve_b = Decimal(str(initial_b))
self.k = self.reserve_a * self.reserve_b
def get_price(self, from_token: str, to_token: str) -> Decimal:
"""Получить текущую цену пары"""
if from_token == self.token_a and to_token == self.token_b:
return self.reserve_b / self.reserve_a
elif from_token == self.token_b and to_token == self.token_a:
return self.reserve_a / self.reserve_b
else:
raise ValueError("Invalid token pair")
def calculate_output_amount(self, input_token: str, input_amount: float) -> Decimal:
"""Рассчитать количество выходного токена при обмене"""
input_amount_dec = Decimal(str(input_amount))
if input_token == self.token_a:
new_reserve_a = self.reserve_a + input_amount_dec
new_reserve_b = self.k / new_reserve_a
output_amount = self.reserve_b - new_reserve_b
elif input_token == self.token_b:
new_reserve_b = self.reserve_b + input_amount_dec
new_reserve_a = self.k / new_reserve_b
output_amount = self.reserve_a - new_reserve_a
else:
raise ValueError("Invalid input token")
# Проверка на достаточность ликвидности
if output_amount <= 0:
raise ValueError("Insufficient liquidity")
return output_amount
def swap(self, input_token: str, input_amount: float) -> Decimal:
"""Выполнить обмен токенов"""
output_amount = self.calculate_output_amount(input_token, input_amount)
input_amount_dec = Decimal(str(input_amount))
# Обновление резервов
if input_token == self.token_a:
self.reserve_a += input_amount_dec
self.reserve_b -= output_amount
else:
self.reserve_b += input_amount_dec
self.reserve_a -= output_amount
# k должен оставаться постоянным (с учетом комиссии)
# В реальных AMM здесь учитывается комиссия
self.k = self.reserve_a * self.reserve_b
return output_amount
def add_liquidity(self, amount_a: float, amount_b: float) -> Decimal:
"""Добавить ликвидность в пул"""
amount_a_dec = Decimal(str(amount_a))
amount_b_dec = Decimal(str(amount_b))
# Проверка соотношения (в реальных AMM это более сложно)
current_ratio = self.reserve_b / self.reserve_a
provided_ratio = amount_b_dec / amount_a_dec
if abs(current_ratio - provided_ratio) > Decimal('0.01'): # Допуск 1%
raise ValueError("Invalid ratio provided")
# Вычисление LP токенов (упрощенно)
total_liquidity = math.sqrt(self.reserve_a * self.reserve_b)
liquidity_minted = (amount_a_dec / self.reserve_a) * total_liquidity
# Обновление резервов
self.reserve_a += amount_a_dec
self.reserve_b += amount_b_dec
self.k = self.reserve_a * self.reserve_b
return liquidity_minted
def get_reserves(self) -> tuple:
"""Получить текущие резервы"""
return float(self.reserve_a), float(self.reserve_b)
# Пример использования
def demonstrate_amm():
# Создаем AMM пул с 1000 ETH и 2000000 USDT (цена 1 ETH = 2000 USDT)
amm = ConstantProductAMM("ETH", "USDT", 1000, 2000000)
print("=== Инициализация пула ===")
print(f"Резервы: {amm.get_reserves()}")
print(f"Цена ETH в USDT: {amm.get_price('ETH', 'USDT'):.2f}")
print(f"Цена USDT в ETH: {amm.get_price('USDT', 'ETH'):.6f}")
print()
# Покупка ETH за USDT
print("=== Покупка 1 ETH за USDT ===")
usdt_amount = 2000 # Ожидаем потратить ~2000 USDT
try:
eth_received = amm.swap("USDT", usdt_amount)
print(f"Получено ETH: {eth_received:.6f}")
print(f"Потрачено USDT: {usdt_amount}")
print(f"Новые резервы: {amm.get_reserves()}")
print(f"Новая цена ETH: {amm.get_price('ETH', 'USDT'):.2f}")
except Exception as e:
print(f"Ошибка: {e}")
print()
# Продажа ETH за USDT
print("=== Продажа 0.5 ETH за USDT ===")
eth_amount = 0.5
try:
usdt_received = amm.swap("ETH", eth_amount)
print(f"Получено USDT: {usdt_received:.2f}")
print(f"Потрачено ETH: {eth_amount}")
print(f"Новые резервы: {amm.get_reserves()}")
print(f"Новая цена ETH: {amm.get_price('ETH', 'USDT'):.2f}")
except Exception as e:
print(f"Ошибка: {e}")
print()
# Добавление ликвидности
print("=== Добавление ликвидности ===")
try:
lp_tokens = amm.add_liquidity(10, 20000) # 10 ETH и 20000 USDT
print(f"Выпущено LP токенов: {lp_tokens:.2f}")
print(f"Новые резервы: {amm.get_reserves()}")
except Exception as e:
print(f"Ошибка: {e}")
if __name__ == "__main__":
demonstrate_amm()
Расширенная реализация с комиссиями
class AdvancedAMM(ConstantProductAMM):
def __init__(self, token_a: str, token_b: str, initial_a: float, initial_b: float, fee: float = 0.003):
super().__init__(token_a, token_b, initial_a, initial_b)
self.fee = Decimal(str(fee))
self.lp_total_supply = Decimal('0')
self.lp_balances = {}
def add_liquidity(self, amount_a: float, amount_b: float, provider: str) -> Decimal:
"""Добавить ликвидность с выпуском LP токенов"""
amount_a_dec = Decimal(str(amount_a))
amount_b_dec = Decimal(str(amount_b))
if self.lp_total_supply == 0:
# Первое пополнение ликвидности
liquidity = math.sqrt(amount_a_dec * amount_b_dec)
else:
# Пропорциональное пополнение
liquidity_a = (amount_a_dec / self.reserve_a) * self.lp_total_supply
liquidity_b = (amount_b_dec / self.reserve_b) * self.lp_total_supply
liquidity = min(liquidity_a, liquidity_b)
if liquidity <= 0:
raise ValueError("Invalid liquidity amount")
# Обновление резервов
self.reserve_a += amount_a_dec
self.reserve_b += amount_b_dec
self.k = self.reserve_a * self.reserve_b
# Выпуск LP токенов
self.lp_total_supply += liquidity
if provider in self.lp_balances:
self.lp_balances[provider] += liquidity
else:
self.lp_balances[provider] = liquidity
return liquidity
def remove_liquidity(self, liquidity: float, provider: str) -> tuple:
"""Удалить ликвидность и получить обратно токены"""
liquidity_dec = Decimal(str(liquidity))
if provider not in self.lp_balances or self.lp_balances[provider] < liquidity_dec:
raise ValueError("Insufficient LP tokens")
# Вычисление доли
share = liquidity_dec / self.lp_total_supply
# Количество возвращаемых токенов
amount_a = share * self.reserve_a
amount_b = share * self.reserve_b
# Обновление резервов и LP токенов
self.reserve_a -= amount_a
self.reserve_b -= amount_b
self.k = self.reserve_a * self.reserve_b
self.lp_balances[provider] -= liquidity_dec
self.lp_total_supply -= liquidity_dec
return float(amount_a), float(amount_b)
def calculate_output_amount_with_fee(self, input_token: str, input_amount: float) -> Decimal:
"""Рассчитать выходное количество с учетом комиссии"""
input_amount_dec = Decimal(str(input_amount))
input_amount_after_fee = input_amount_dec * (Decimal('1') - self.fee)
if input_token == self.token_a:
new_reserve_a = self.reserve_a + input_amount_after_fee
new_reserve_b = self.k / new_reserve_a
output_amount = self.reserve_b - new_reserve_b
else:
new_reserve_b = self.reserve_b + input_amount_after_fee
new_reserve_a = self.k / new_reserve_b
output_amount = self.reserve_a - new_reserve_a
return output_amount
# Демонстрация расширенного AMM
def demonstrate_advanced_amm():
amm = AdvancedAMM("ETH", "USDT", 100, 200000, 0.003) # 0.3% комиссия
print("=== Расширенный AMM с комиссиями ===")
# Добавляем ликвидность
lp_tokens = amm.add_liquidity(10, 20000, "provider1")
print(f"Добавлена ликвидность, выпущено LP токенов: {lp_tokens:.2f}")
# Обмен с комиссией
print("\n=== Обмен с комиссией ===")
usdt_amount = 1000
eth_received = amm.calculate_output_amount_with_fee("USDT", usdt_amount)
print(f"При обмене {usdt_amount} USDT получим {eth_received:.6f} ETH")
# Выполняем обмен
actual_eth = amm.swap("USDT", usdt_amount)
print(f"Фактически получено: {actual_eth:.6f} ETH")
print(f"Новые резервы: {amm.get_reserves()}")
# demonstrate_advanced_amm()
Кривая ликвидности и проскальзывание
import matplotlib.pyplot as plt
import numpy as np
def plot_liquidity_curve():
"""Визуализация кривой ликвидности"""
# Параметры пула
k = 1000000 # константа
x = np.linspace(100, 1000, 100)
y = k / x
plt.figure(figsize=(10, 6))
plt.plot(x, y, 'b-', linewidth=2, label='Кривая ликвидности x*y=k')
plt.xlabel('Количество токена A')
plt.ylabel('Количество токена B')
plt.title('Кривая ликвидности AMM')
plt.grid(True, alpha=0.3)
plt.legend()
# Показываем точку текущего баланса
current_x, current_y = 500, 2000
plt.plot(current_x, current_y, 'ro', markersize=8, label='Текущий баланс')
plt.annotate(f'({current_x}, {current_y})',
(current_x, current_y),
textcoords="offset points",
xytext=(0,10),
ha='center')
plt.show()
def calculate_slippage(amm, input_token: str, input_amount: float):
"""Расчет проскальзывания для сделки"""
spot_price = amm.get_price(input_token, amm.token_b if input_token == amm.token_a else amm.token_a)
if input_token == amm.token_a:
output_amount = amm.calculate_output_amount(input_token, input_amount)
effective_price = Decimal(str(input_amount)) / output_amount
else:
output_amount = amm.calculate_output_amount(input_token, input_amount)
effective_price = output_amount / Decimal(str(input_amount))
slippage = (effective_price - spot_price) / spot_price * 100
return {
'spot_price': float(spot_price),
'effective_price': float(effective_price),
'slippage_percent': float(slippage),
'output_amount': float(output_amount)
}
# Пример анализа проскальзывания
def slippage_analysis():
amm = ConstantProductAMM("ETH", "USDT", 1000, 2000000)
print("=== Анализ проскальзывания ===")
trade_sizes = [100, 1000, 10000, 50000] # USDT
for size in trade_sizes:
result = calculate_slippage(amm, "USDT", size)
print(f"\nРазмер сделки: {size} USDT")
print(f"Спот цена: {result['spot_price']:.2f}")
print(f"Эффективная цена: {result['effective_price']:.2f}")
print(f"Проскальзывание: {result['slippage_percent']:.4f}%")
print(f"Получено ETH: {result['output_amount']:.6f}")
# plot_liquidity_curve()
# slippage_analysis()
Ключевые особенности AMM:
- Доступность - кто угодно может стать поставщиком ликвидности
- Автоматизация - цены определяются алгоритмически
- Постоянная ликвидность - торговля возможна в любое время
- Имперманентная потеря - риск для поставщиков ликвидности
Популярные реализации:
- Uniswap (константный продукт)
- Curve (стабильные пулы)
- Balancer (пулы с несколькими токенами)
- Bancor (пулы с одной стороной)