Разработка системы автоматической ребалансировки LP Uniswap v3
Concentrated liquidity в Uniswap v3 — это двойственная природа. Провайдер ликвидности с узким диапазоном собирает в 10-50 раз больше fee, чем в v2. Но как только цена выходит за диапазон — позиция становится 100% в одном активе и перестаёт зарабатывать. ETH/USDC позиция с диапазоном $1800-$2200 при цене ETH $2500 — это чистый USDC bag, ноль fee. Без автоматического управления провайдер теряет доходность на каждом крупном движении.
Задача системы автоматической ребалансировки: отслеживать позиции, определять момент выхода цены за диапазон, собирать fee, закрывать позицию, открывать новую вокруг текущей цены — атомарно или через keeper.
Механика позиции Uniswap v3
NFT-позиции и NonfungiblePositionManager
Каждая LP-позиция в v3 — это ERC-721 токен, управляемый NonfungiblePositionManager (адрес 0xC36442b4a4522E871399CD717aBDD847Ab11FE88 на mainnet). Основные операции:
-
mint()— открытие позиции в заданном диапазоне[tickLower, tickUpper] -
increaseLiquidity()— добавление ликвидности в существующую позицию -
decreaseLiquidity()— частичное или полное изъятие ликвидности -
collect()— сбор накопленных fee токенов
Для автоматической ребалансировки контракт-менеджер должен владеть NFT-позициями (или иметь approval) — это позволяет ему вызывать все операции атомарно.
Tick math и диапазоны
Цена в v3 хранится в sqrtPriceX96 — квадратный корень цены в Q64.96 fixed-point формате. Для расчёта текущего тика: tick = floor(log(price) / log(1.0001)). Каждый тик соответствует шагу цены в 0.01%.
Диапазон позиции задаётся в тиках и должен быть кратен tickSpacing пула. Для пула 0.05% fee — tickSpacing 10, для 0.3% — 60, для 1% — 200. Позиция с шириной 200 тиков вокруг текущей цены покрывает примерно ±1% движения.
Стратегии ребалансировки
Passive rebalance (after range exit)
Простейшая стратегия: ждём пока цена выйдет за диапазон, собираем fee, закрываем позицию, открываем новую с центром на текущей цене. Минус: impermanent loss зафиксирован, позиция была out-of-range часть времени.
Active rebalance (before range exit)
Ребалансируем когда цена приближается к краю диапазона — например, когда осталось 20% ширины до границы. Это позволяет остаться in-range и продолжать зарабатывать fee. Минус: частые ребалансировки на волатильном рынке — высокие gas costs и потери на свапах.
Asymmetric ranges
Вместо симметричного диапазона [price * 0.9, price * 1.1] — асимметричный с учётом тренда. На бычьем рынке для ETH/USDC: [price * 0.95, price * 1.20] — больший диапазон сверху. Параметры тренда берём из EMA off-chain и передаём в keeper как performData.
Fee reinvestment
Собранные fee автоматически реинвестируются в ту же позицию (compound). Это требует предварительного свапа fee-токенов в правильное соотношение для текущего диапазона — off-chain расчёт соотношения через Uniswap SDK, on-chain исполнение через exactInputSingle.
Архитектура контракта
AutoLPManager
├── positionsByUser: mapping(address => uint256[]) — tokenIds
├── strategyByToken: mapping(uint256 => RebalanceStrategy)
├── rebalance(uint256 tokenId, RangeParams calldata newRange) — keeper вызов
├── collect(uint256 tokenId) — сбор fee
└── compound(uint256 tokenId) — reinvest fee
Ключевая проблема: свап перед открытием позиции. Для открытия позиции в диапазоне [tickLower, tickUpper] нужно точное соотношение token0/token1. Если после закрытия позиции у нас 100% USDC (цена выросла), а для новой позиции нужно 50/50 ETH/USDC — нужен предварительный свап. Свап меняет соотношение, что может сдвинуть тик, что меняет требуемое соотношение... итерационный расчёт off-chain, и только потом on-chain исполнение.
Альтернатива: использовать готовые vault-решения как основу. Arrakis Finance v2 и Gamma Strategies — open source vault контракты с именно такой логикой. Форкнуть и кастомизировать под нужную стратегию быстрее, чем писать с нуля.
Мониторинг и keeper
Off-chain сервис (Node.js + viem) мониторит:
- Текущий тик пула:
slot0()→sqrtPriceX96→ конвертация в тик - Тики позиции:
positions(tokenId)→tickLower,tickUpper - Накопленные fee: симуляция
collect()черезeth_call
При срабатывании условия ребалансировки — вызов через Chainlink Automation или собственный keeper EOA с достаточным gas buffer.
Gas оценки на mainnet:
| Операция | Gas |
|---|---|
| collect() fee | ~90k |
| decreaseLiquidity() 100% | ~120k |
| Swap token0 → token1 (v3) | ~150k |
| mint() новая позиция | ~180k |
| Итого полный rebalance |
На L2 (Arbitrum, Base) те же операции стоят в 20-50 раз дешевле, что делает частые ребалансировки экономически осмысленными.
Процесс разработки
Аналитика стратегии (2-3 дня). Backtesting выбранной стратегии на исторических данных Uniswap v3. Для ETH/USDC 0.05% пула — данные доступны через The Graph, Dune Analytics.
Контракты (1-2 недели). AutoLPManager + keeper-совместимые интерфейсы. Foundry fork-тесты на mainnet: симулируем несколько ребалансировок через крупные ценовые движения.
Off-chain компоненты (4-5 дней). Мониторинг позиций, расчёт optimal ratio для свапа перед mint, Chainlink Automation setup.
Тестирование (3-5 дней). Сравнение APY при разных параметрах стратегии на исторических данных.
Ориентиры по срокам
Базовая система с одной стратегией на один пул — 1-2 недели. Multi-pool, multi-strategy vault с governance — от 3-4 недель.
Стоимость рассчитывается после выбора целевых пулов и стратегий ребалансировки.







