Розробка системи управління concentrated liquidity
Uniswap V3 запустився в травні 2021 з ідеєю, яка здавалась очевидною тільки після пояснення: чому розміщувати ліквідність по всьому ціновому діапазону від 0 до нескінченності, якщо реальна торгівля відбувається в діапазоні ±10–20% від поточної ціни? Concentrated liquidity дозволяє LP вибрати діапазон [tickLower, tickUpper] та забезпечити в ньому в 100–1000x більше глибини при тому ж капіталі.
Проблема: позиція заробляє fee тільки поки ціна в діапазоні. Як тільки ціна вийде за границі — LP повністю конвертується в один актив та перестає приносити доход. Управління такими позиціями вручну — заняття для ботів, не для людей.
Чому пасивна позиція в V3 збиткова на волатильних парах
Класичний backtesting по даних Uniswap V3 (2021–2024) показує неприємну картину: LP у широкому діапазоні на ETH/USDC заробляє на fee менше, ніж втрачає на impermanent loss при типовій волатильності ETH. Hold обох активів outperforms пасивного LP.
Чому? При русі ціни на 20% від центру діапазону позиція починає відчувати accelerated IL — ефект посилення impermanent loss порівняно з V2. Той же капітал, сконцентрований у 5x вужчий діапазон, заробляє у 5x більше fee, але гораздо частіше опиняється out-of-range.
Єдиний спосіб вийти в плюс — активне управління: переміщувати діапазон слідом за ціною, балансуючи між fee APR та газовими затратами на ребалансування. Це й є завдання системи управління.
Архітектура системи: три компоненти
1. On-chain Vault контракт
Vault — це смарт-контракт, який володіє NFT позиціями Uniswap V3 (ERC-721) від імені користувачів. Користувачі депонують tokenA та tokenB, отримують shares vault. Vault самостійно керує позицією.
interface IConcentratedLiquidityVault {
function deposit(uint256 amount0, uint256 amount1, address recipient)
external returns (uint256 shares);
function withdraw(uint256 shares, address recipient)
external returns (uint256 amount0, uint256 amount1);
function rebalance(
int24 newTickLower,
int24 newTickUpper,
uint256 swapAmount,
bool zeroForOne
) external;
function collectFees() external returns (uint256 fees0, uint256 fees1);
}
Ключовий момент: функція rebalance викликається зовнішнім keeper-ботом. Вона:
- Видаляє всю ліквідність з поточної позиції (
decreaseLiquidity+collectчерез NonfungiblePositionManager) - Опціонально свапає частину активів для балансування в потрібне співвідношення
- Створює нову позицію в новому діапазоні (
mint)
Авторизація на вивізит rebalance — через whitelist keeper адрес з Gnosis Safe мультисиг для управління списком.
2. Off-chain стратегічний движок
Node.js (або Python) сервіс, який безперервно моніторить стан позиції та приймає рішення про ребалансування.
Вхідні дані:
- Поточна ціна (з Uniswap V3 pool
slot0.sqrtPriceX96) - Поточний діапазон позиції (
tickLower,tickUpper) - Накопичені fees (ще не зібрані)
- Історичні дані по волатильності (для розрахунку оптимальної ширини діапазону)
Стратегії ребалансування:
Пороговая стратегія — ребалансування коли ціна відклонилась від центру діапазону на X%. Просто, передбачувано, але не враховує volatility regime.
Volatility-based стратегія — ширина діапазону = N * σ (стандартне відхилення ціни за останні K днів). При високій волатильності діапазон ширше (менше ребалансувань, краще IL protection), при низькій — вужче (більше fee). N зазвичай від 1.5 до 3 стандартних відхилень.
Backtesting по історичним даним обов'язковий перед деплоєм. Беремо історичні ціни з The Graph або Uniswap V3 subgraph, симулюємо стратегію, рахуємо:
- Суммарні fee за період
- Суммарний IL
- Gas costs на ребалансування
- Net P&L vs hold
interface RebalanceSignal {
shouldRebalance: boolean
newTickLower: number
newTickUpper: number
swapAmount: bigint
zeroForOne: boolean
reason: 'out_of_range' | 'drift' | 'volatility_change'
}
function evaluateRebalance(
currentTick: number,
tickLower: number,
tickUpper: number,
volatility30d: number
): RebalanceSignal {
const rangeWidth = tickUpper - tickLower
const center = (tickLower + tickUpper) / 2
const drift = Math.abs(currentTick - center) / (rangeWidth / 2)
// Ребалансування якщо ціна ушла на 80% від центру до краю діапазону
if (drift > 0.8 || currentTick < tickLower || currentTick > tickUpper) {
const optimalRange = Math.round(volatility30d * 2 * 10000) // ticks
return {
shouldRebalance: true,
newTickLower: Math.round(currentTick - optimalRange / 2),
newTickUpper: Math.round(currentTick + optimalRange / 2),
// ... swap calculation
}
}
return { shouldRebalance: false, ... }
}
3. Keeper інфраструктура
Keeper — це сервіс, який викликає rebalance() на-чейні при отриманні сигналу від стратегічного движка. Вимоги до keeper:
- Висока uptime (99.9%+). Позиція out-of-range не приносить fee.
- Atomicity: ребалансування має завершитися або цілком откатитися. Не можна опинитися в стану «ліквідність видалена, нова позиція не створена».
- MEV protection: ребалансування включає swap, який видно у mempool. Потрібно використовувати Flashbots private relay щоб не бути sandwich-атакованим.
Відправка через Flashbots:
const bundle = await flashbotsProvider.sendBundle(
[{ signer: keeperWallet, transaction: rebalanceTx }],
targetBlock,
{ minTimestamp: 0, maxTimestamp: Math.floor(Date.now() / 1000) + 30 }
)
Gas оптимізація ребалансування
Одна ребалансування на Ethereum mainnet коштує 300,000–500,000 gas (видалення + збір fees + swap + нова позиція). При 20 gwei gas та ETH $3000 — $18–30 за операцію.
Для малих позицій (<$10,000) це робить ребалансування при частій частоті нерентабельною. Рішення: підтримка L2 (Arbitrum, Optimism, Base) з Uniswap V3 deployment. На Arbitrum gas у 100x дешевше — ребалансування коштує $0.20–0.50.
Додаткова оптимізація: компаундинг зібраних fees назад у позицію в рамках тієї ж ребалансування транзакції — замість окремого collect + mint.
Uniswap V4 та Hooks
Uniswap V4 вводить Hooks — можливість встроїти логіку управління прямо у пул через hook контракт. beforeSwap та afterSwap хуки дозволяють динамічно змінювати fee залежно від умов. Для системи управління ліквідністю це відкриває можливість реалізувати динамічний fee range: ширший діапазон з меншою комісією при високій волатильності, вужчий з високою — при низькій.
Архітектурно чистіше, ніж off-chain keeper, але вимагає написання hook контракту та окремого аудиту від vault логіки.
Процес розробки
Дослідження стратегій (3–5 днів). Backtesting кількох стратегій на історичних даних обраної пари. Оцінка оптимальних параметрів ширини діапазону та триггерів ребалансування.
Розробка vault контракту (2–3 тижні). Solidity, Foundry тести з fork-тестуванням на реальних Uniswap V3 mainnet даних.
Off-chain движок (1–2 тижні). Стратегічний движок, keeper бот, моніторинг.
Фронтенд (1–2 тижні). Deposit/withdraw UI, відображення поточної позиції та історичної доходності.
Аудит. Рекомендуємо зовнішній аудит vault контракту перед привласнення значимої ліквідності.
Орієнтири по строкам
Базовий vault для однієї пари з пороговою стратегією — 4–6 тижнів. Мультипарний vault з volatility-based стратегією, Arbitrum/Optimism підтримкою та аналітичним дашбордом — 10–14 тижнів.







