How to Earn from Uniswap V3 Liquidity Pools: Complete Strategy Guide
Uniswap V3 liquidity provision can earn 15–60% APY — but only if you manage positions correctly. This guide covers concentrated liquidity, impermanent loss, auto-rebalancing bots, and the strategies that actually make money.
Builder of AI agents, crypto trading bots, and open-source automation tools. Sharing practical guides on how to build, deploy, and profit from AI and DeFi technology.
Why Uniswap V3 Is Different
In Uniswap V2, you provided liquidity across the entire price range (0 to infinity). This was simple but capital-inefficient.
In Uniswap V3, you choose a specific price range. All your capital concentrates in that range, earning a much larger fraction of fees when trades happen there.
Same capital. 10–100x more fee income. But you need to actively manage your position.
How Fee Earnings Work
Uniswap V3 pools have three fee tiers:
- 0.01% — Stablecoin/stablecoin pairs (USDC/USDT)
- 0.05% — Stable-like pairs (USDC/ETH)
- 0.3% — Standard pairs (ETH/BTC)
- 1% — Exotic/volatile pairs
Every trade that passes through your price range earns you a proportional share of those fees.
Example: The USDC/ETH 0.05% pool trades $500M/day. If you own 0.1% of the liquidity in range, you earn $250/day.
The Core Challenge: Impermanent Loss
When the price moves outside your range, you stop earning fees AND your position shifts entirely to the falling asset.
Example with ETH/USDC:
- You provide liquidity from $3,000 to $3,500
- ETH rises to $3,600 → Your entire position converts to USDC
- ETH then rises to $4,000
- You missed $400 of ETH appreciation (impermanent loss)
This is why Uniswap V3 requires active management. Set-and-forget loses to simply holding the assets.
Strategy 1: Stablecoin Pairs (Lowest Risk)
The easiest entry point: provide USDC/USDT liquidity.
- No impermanent loss (both assets stay at ~$1)
- Pure fee income: 0.01–0.05% on every trade
- Range: Set tight range like $0.997–$1.003
const { ethers } = require('ethers')
const { Pool, Position, NonfungiblePositionManager } = require('@uniswap/v3-sdk')
const { Token } = require('@uniswap/sdk-core')
const USDC = new Token(1, '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 6, 'USDC')
const USDT = new Token(1, '0xdAC17F958D2ee523a2206206994597C13D831ec7', 6, 'USDT')
// Mint a new position in the USDC/USDT 0.01% pool
async function addStablecoinLiquidity(amount_usdc, amount_usdt) {
const pool = await getPool(USDC, USDT, FeeAmount.LOWEST) // 0.01%
// Use a very tight range around $1
const position = Position.fromAmounts({
pool,
tickLower: nearestUsableTick(-100, pool.tickSpacing), // ~$0.990
tickUpper: nearestUsableTick(100, pool.tickSpacing), // ~$1.010
amount0: ethers.parseUnits(amount_usdc.toString(), 6),
amount1: ethers.parseUnits(amount_usdt.toString(), 6),
useFullPrecision: true,
})
// Submit mint transaction
const { calldata, value } = NonfungiblePositionManager.addCallParameters(position, {
slippageTolerance: new Percent(50, 10000), // 0.5% slippage
recipient: wallet.address,
deadline: Math.floor(Date.now() / 1000) + 60 * 20, // 20 minutes
})
return wallet.sendTransaction({ to: POSITION_MANAGER_ADDRESS, data: calldata, value })
}
Expected APY: 10–20% on stablecoin pairs during high-volume periods.
Strategy 2: ETH/USDC with Auto-Rebalancing Bot
For non-stable pairs, you need a bot to rebalance your position as price moves.
from web3 import Web3
from uniswap import Uniswap
import time
w3 = Web3(Web3.HTTPProvider(os.getenv('RPC_URL')))
uniswap = Uniswap(address=MY_ADDRESS, private_key=MY_KEY, web3=w3, version=3)
ETH = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" # WETH
USDC = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
RANGE_WIDTH_PCT = 0.20 # Provide liquidity ±10% around current price
def get_current_price():
"""Get ETH/USDC price from Uniswap pool."""
return uniswap.get_price_input(ETH, USDC, 10**18) / 10**6
def is_in_range(lower, upper, current):
"""Check if current price is within our range."""
# Add 5% buffer before rebalancing
buffer = (upper - lower) * 0.05
return lower + buffer < current < upper - buffer
def calculate_new_range(current_price):
"""Calculate new range centered on current price."""
half_width = current_price * (RANGE_WIDTH_PCT / 2)
return current_price - half_width, current_price + half_width
class LiquidityManager:
def __init__(self):
self.position_id = None
self.lower_price = None
self.upper_price = None
def rebalance(self):
"""Remove existing position and create new one centered at current price."""
current = get_current_price()
if self.position_id and is_in_range(self.lower_price, self.upper_price, current):
print(f"Price ${current:.2f} — still in range ${self.lower_price:.2f}-${self.upper_price:.2f}")
return
# Remove old position (collect fees + remove liquidity)
if self.position_id:
print(f"Price ${current:.2f} left range. Rebalancing...")
self.collect_and_remove()
# Create new position
new_lower, new_upper = calculate_new_range(current)
self.position_id = self.add_liquidity(new_lower, new_upper)
self.lower_price = new_lower
self.upper_price = new_upper
print(f"New position: ${new_lower:.2f} - ${new_upper:.2f}")
def collect_and_remove(self):
"""Collect accumulated fees and remove liquidity."""
# Collect fees first
uniswap.collect_fees(self.position_id)
# Remove liquidity
uniswap.remove_liquidity(self.position_id, remove_percent=100)
self.position_id = None
def add_liquidity(self, lower_price, upper_price):
"""Add new liquidity position."""
balance_eth = w3.eth.get_balance(MY_ADDRESS) / 10**18 * 0.9
balance_usdc = uniswap.get_token_balance(USDC) / 10**6 * 0.9
return uniswap.add_liquidity_new_position(
token0=ETH,
token1=USDC,
fee=500, # 0.05% fee tier
lower_price=lower_price,
upper_price=upper_price,
amount0=balance_eth,
amount1=balance_usdc,
)
manager = LiquidityManager()
while True:
try:
manager.rebalance()
time.sleep(300) # Check every 5 minutes
except Exception as e:
print(f"Error: {e}")
time.sleep(60)
Understanding Your Returns
Two components of return:
- Fee income: The trading fees you collect (good)
- Impermanent loss: The opportunity cost vs. just holding (bad)
Net return = Fee income − Impermanent loss
For ETH/USDC with 20% range width:
- Fee income: 15–40% APY in active markets
- Impermanent loss: 2–15% per rebalance
- Net: 5–30% APY depending on market conditions
When this strategy works best:
- High trading volume (more fees)
- Low price volatility (less IL)
- You rebalance before price leaves range by too much
Tools for Monitoring Positions
- Revert Finance — Best dashboard for Uniswap V3 analytics
- DeFi Lab — Shows fee APY for your specific position
- Gamma Strategies — Automated rebalancing vaults
FAQ
Q: How much do I need to start? A: Minimum $500, but $2,000+ makes the gas costs worthwhile.
Q: Is impermanent loss permanent? A: Only if you remove liquidity while the price has moved. If price returns to your range, IL reverses.
Q: Can I automate this on a small budget? A: Yes — our DeFi bot on the Tools page handles position monitoring and rebalancing for ETH/USDC automatically.
Q: Which fee tier should I use? A: ETH/USDC → 0.05% (most volume). New or volatile pairs → 0.3% or 1%.
Get the Automated Bot
Our DeFi Yield Optimizer handles Uniswap V3 position management automatically — monitoring, rebalancing, and compounding fees. Available on the Tools page.
Tagged in
Related Articles
Uniswap V4 Hooks: The New DeFi Bot Primitive Every Developer Must Know
5 min read
DeFiEthereum Layer 2 Trading Bot Comparison: Arbitrum vs Base vs Optimism
5 min read
DeFiSolana vs Ethereum for DeFi Bots: Which Blockchain Wins in 2026?
4 min read
DeFiWhat Are Real World Assets (RWA) in DeFi and How Bots Can Trade Them
5 min read