A sophisticated toolkit for collecting Uniswap V3 pool data and backtesting dynamic liquidity provision strategies with realistic trading costs, impermanent loss, and comprehensive performance metrics.
This code is deep in the trenches of developmentβnot production-ready.
If you're not a Python chad or a true DeFi degen, stay away before you get rekt.
Touch at your own risk. π₯πΈπ
- Overview
- Features
- Installation
- Usage
- Strategy Types
- Performance Metrics
- Output Files
- Analyzing Results
- Examples
- Contributing
- License
This project provides tools to analyze Uniswap V3 pools and develop optimized liquidity provision strategies. It consists of two main components:
- Pool Data Collector (
pool_data.py
): Collects comprehensive historical data from Uniswap V3 pools, including trading volume and tick-specific liquidity. - Liquidity Strategy (
liquidity_strategy.py
): Implements and backtests various liquidity provision strategies with realistic modeling of fees, slippage, and transaction costs.
The toolkit helps liquidity providers optimize their capital efficiency by determining when to rebalance positions and how to size position ranges based on historical volatility, with full portfolio value tracking and advanced performance metrics.
- Robust RPC Management:
- Automatic failover between multiple RPC providers
- Smart rate limit detection and cooldown periods
- Exponential backoff retry mechanism
- Prioritization of reliable RPC endpoints
- Optimized Data Collection:
- Multicall batching with dynamic batch sizing
- Result caching to reduce redundant RPC calls
- Adaptive sampling based on price volatility
- Parallel processing with configurable worker threads
- Comprehensive Data Gathering:
- Historical price and liquidity data collection
- Trading volume data from Uniswap Subgraph API
- Tick-specific liquidity data for accurate fee estimation
- Impermanent loss calculation
- Volatility estimation
- Active ticks analysis
- Performance Optimizations:
- Block grouping by proximity for efficient querying
- Automatic batch size adjustment based on historical performance
- Memory-efficient caching with cleanup mechanisms
- Graceful degradation for rate-limited environments
- Data Visualization and Export:
- Interactive price and liquidity charts
- Exportable datasets for further analysis
- Metadata preservation for reproducibility
- Portfolio Value Tracking:
- Initial capital configuration
- Realistic portfolio value calculation over time
- Support for USD-based performance tracking
- Rebalancing Cost Models:
- Swap slippage based on position size and liquidity
- Gas cost modeling
- DEX trading fees
- Fee Calculations:
- Realistic fee assessments based on position-to-pool ratio
- Volume-based fee estimation
- Tick-specific liquidity concentration
- Advanced Performance Metrics:
- Maximum drawdown and recovery analysis
- Sortino ratio (downside risk adjustment)
- Calmar ratio (return relative to drawdown)
- Win/loss metrics (win rate, profit factor)
- Detailed rebalance event analysis
- Strategy Comparison:
- Side-by-side comparison of multiple strategies
- Conservative, moderate, and aggressive presets
- Trigger analysis (time vs. price vs. volatility)
- Visualization:
- Portfolio value over time
- Maximum drawdown periods
- Performance attribution (fees, IL, costs)
- Rebalance events by trigger type
- Clone the repository:
git clone /~https://github.com/ilyamk/uniswap-v3-lp-strategy-toolkit.git
cd uniswap-v3-lp-strategy-toolkit
- Install dependencies:
pip install -r requirements.txt
- Set up your Ethereum RPC provider:
- Get an API key from Alchemy
- Create a
.env
file with your API key:
ALCHEMY_API_KEY=your_api_key_here THEGRAPH_API_KEY=your_graph_api_key_here # Optional, for volume data
The pool_data.py
script collects historical data from a Uniswap V3 pool with robust RPC management and optimized data collection.
python pool_data.py --pool <pool_address> --days <days_to_collect> --ticks --volume --export-ticks
Option | Description | Default |
---|---|---|
--pool |
Uniswap V3 pool address | wstETH/ETH 0.01% pool |
--days |
Number of days to collect data for | 365 |
--step |
Block step for data collection | 1000 |
--output |
Base name for output files | uniswap_v3_data |
--ticks |
Include active ticks data (slower) | False |
--no-ticks |
Explicitly disable active ticks collection | False |
--backtest |
Run basic strategy backtesting | False |
--volume |
Fetch trading volume data from Uniswap Subgraph | False |
--export-ticks |
Export tick data in a format usable by strategy script | False |
--adaptive |
Enable adaptive sampling based on volatility | True |
--no-adaptive |
Disable adaptive sampling | False |
--parallel |
Enable parallel processing, if you have high RPC rate limit | True |
--no-parallel |
Disable parallel processing, if you have low RPC rate limit of free plan | False |
--workers |
Number of worker threads for parallel processing | 4 |
--batch-size |
Batch size for parallel processing | 100 |
--tick-range |
Number of ticks to check on each side of current tick | 10 |
--multicall-batch-size |
Batch size for multicall operations | 10 |
Basic Data Collection:
python pool_data.py --pool 0x109830a1AAaD605BbF02a9dFA7B0B92EC2FB7dAa --days 90 --output wsteth_eth_data
This collects 90 days of basic price and liquidity data for the wstETH/ETH pool without tick-specific data.
Comprehensive Data Collection:
python pool_data.py --pool 0x109830a1AAaD605BbF02a9dFA7B0B92EC2FB7dAa --days 90 --ticks --volume --export-ticks --output wsteth_eth_data
This collects 90 days of data including tick-specific liquidity and trading volume, and exports the tick data for use in strategy backtesting.
High-Performance Collection:
python pool_data.py --pool 0x109830a1AAaD605BbF02a9dFA7B0B92EC2FB7dAa --days 30 --parallel --workers 8 --batch-size 200 --output high_perf_data
This uses parallel processing with 8 worker threads and larger batch sizes to speed up data collection for 30 days of history.
Detailed Tick Analysis:
python pool_data.py --pool 0x109830a1AAaD605BbF02a9dFA7B0B92EC2FB7dAa --days 14 --ticks --tick-range 30 --output detailed_tick_data
This collects 14 days of data with an expanded tick range (30 ticks on each side of the current price) for more detailed liquidity analysis.
Low-Resource Collection:
python pool_data.py --pool 0x109830a1AAaD605BbF02a9dFA7B0B92EC2FB7dAa --days 180 --no-adaptive --no-parallel --step 2000 --output low_resource_data
This collects 180 days of data with larger block steps and disabled adaptive sampling and parallel processing, suitable for systems with limited resources.
Collection with Immediate Backtesting:
python pool_data.py --pool 0x109830a1AAaD605BbF02a9dFA7B0B92EC2FB7dAa --days 60 --ticks --volume --backtest --output backtest_ready_data
This collects 60 days of data and immediately runs a basic strategy backtest on the collected data.
RPC-Friendly Collection:
python pool_data.py --pool 0x109830a1AAaD605BbF02a9dFA7B0B92EC2FB7dAa --days 30 --multicall-batch-size 5 --batch-size 50 --output rpc_friendly_data
This uses smaller batch sizes for multicall operations and parallel processing to reduce the load on the RPC provider and avoid rate limiting.
The liquidity_strategy.py
script implements and backtests liquidity provision strategies with comprehensive performance metrics.
python liquidity_strategy.py --data <pool_data_file> --strategy <strategy_type> --initial-capital <starting_usd> --daily-volume <avg_volume>
Option | Description | Default |
---|---|---|
--data |
Pool data CSV file | uniswap_v3_data_wstETH_WETH_0_pool_data.csv |
--il |
Impermanent loss data CSV file | uniswap_v3_data_wstETH_WETH_0.01pct_impermanent_loss.csv |
--tick-range |
Tick range for liquidity provision | 10 |
--interval |
Rebalance interval: 'hourly', 'daily', 'weekly', or seconds | daily |
--price-threshold |
Price change threshold for rebalancing in percent | 0.5 |
--volatility-threshold |
Volatility threshold for rebalancing | 1.0 |
--output |
Base name for output files | strategy_results |
--strategy |
Strategy type: 'time' or 'adaptive' | adaptive |
--initial-capital |
Initial capital in USD | 100,000 |
--daily-volume |
Average daily trading volume in USD | None |
--tick-data |
Path to tick data JSON file | None |
--gas-price |
Gas price in Gwei for transaction cost calculation | 30.0 |
--compare-strategies |
Compare multiple strategy presets | False |
Time-based strategy with daily rebalancing and initial capital:
python liquidity_strategy.py --data wsteth_eth_data_0.01_pool_data.csv --strategy time --interval daily --tick-range 15 --initial-capital 100000 --output time_daily_results
Adaptive strategy with realistic fees and trading costs:
python liquidity_strategy.py --data wsteth_eth_data_0.01_pool_data.csv --strategy adaptive --interval daily --price-threshold 0.1 --initial-capital 100000 --daily-volume 5000000 --tick-data wsteth_eth_data_tick_data.json --output adaptive_results
Compare multiple strategy presets:
python liquidity_strategy.py --data wsteth_eth_data_0.01_pool_data.csv --initial-capital 100000 --compare-strategies --output strategy_comparison
Rebalances positions at fixed time intervals (hourly, daily, weekly, or custom). This strategy is simple but effective in many market conditions.
Combines multiple rebalancing triggers:
- Time-based: Rebalance after a fixed time interval
- Price-based: Rebalance when price moves beyond a threshold
- Volatility-based: Rebalance during high volatility periods
Both strategies dynamically adjust the position range based on recent price volatility, which helps optimize capital efficiency.
The toolkit calculates a comprehensive set of performance metrics:
- Total Return: Overall percentage return of the strategy
- Annualized Return: Return projected to an annual basis
- Fees Collected: Total fees earned from providing liquidity
- Impermanent Loss: Loss incurred due to price divergence
- Net Return: Fees minus IL and transaction costs
- Sharpe Ratio: Risk-adjusted return using standard deviation
- Sortino Ratio: Risk-adjusted return penalizing only downside volatility
- Calmar Ratio: Return relative to maximum drawdown
- Maximum Drawdown: Largest peak-to-trough decline in portfolio value
- Drawdown Duration: Length of maximum drawdown period
- Recovery Time: Time taken to recover from maximum drawdown
- Win Rate: Percentage of periods with positive returns
- Profit Factor: Ratio of gross profits to gross losses
- Win/Loss Ratio: Average win amount divided by average loss amount
- Average Position Duration: Average time positions are held
These metrics allow for comprehensive strategy evaluation and comparison.
<output>_pool_data.csv
: Historical pool data (price, liquidity, volume)<output>_impermanent_loss.csv
: Calculated impermanent loss data<output>_metadata.json
: Pool metadata including token details and collection parameters<output>_plot.png
: Price and liquidity visualization<output>_tick_data.json
: Active ticks data formatted for strategy use<output>_ticks_data.csv
: Detailed tick-specific liquidity data (if collected)
<output>_rebalance_events.csv
: Details of each rebalancing event including triggers and costs<output>_metrics.json
: Comprehensive strategy performance metrics<output>_plot.png
: Enhanced strategy visualization with portfolio value and performance attribution
The enhanced backtesting includes:
- Initial & Final Capital: Starting capital and final portfolio value
- Total Return & Annualized Return: Overall and annualized performance
- Components Breakdown:
- Fees Collected
- Impermanent Loss
- Rebalancing Costs (swap fees, slippage, gas)
- Risk Metrics:
- Sharpe, Sortino, and Calmar Ratios
- Maximum Drawdown (percentage and duration)
- Win/Loss Metrics:
- Win Rate
- Profit Factor
- Win/Loss Ratio
- Rebalancing Analysis:
- Total Rebalances
- Trigger Distribution (time, price, volatility)
- Average Time Between Rebalances
The <output>_rebalance_events.csv
file contains detailed information:
Column | Description |
---|---|
timestamp |
Unix timestamp of the rebalance |
datetime |
Human-readable date and time |
old_range_low |
Lower tick of previous position |
old_range_high |
Upper tick of previous position |
new_range_low |
Lower tick of new position |
new_range_high |
Upper tick of new position |
price |
Token price at rebalance time |
trigger |
What triggered the rebalance (time, price, volatility) |
time_since_last |
Seconds since previous rebalance |
price_change_pct |
Percentage price change since last rebalance |
volatility |
Estimated daily volatility at rebalance time |
rebalance_cost |
Total cost of the rebalance operation |
swap_fee |
DEX trading fee for the rebalance |
slippage |
Slippage cost during rebalance |
gas_cost |
Gas cost in USD for the transaction |
portfolio_value |
Portfolio value after rebalance |
The <output>_plot.png
file provides a comprehensive visual representation:
- Price Panel: Price chart with rebalance events marked by trigger type
- Liquidity Panel: Pool liquidity over time
- Portfolio Value Panel: Portfolio value evolution with drawdown periods
- Performance Attribution Panel: Cumulative returns, fees, IL, and costs
# Collect comprehensive data including volume and tick data
python pool_data.py --pool 0x109830a1AAaD605BbF02a9dFA7B0B92EC2FB7dAa --days 30 --ticks --volume --export-ticks --output wsteth_eth_data
This command collects 30 days of historical data, trading volume, and tick-specific liquidity.
# Run adaptive strategy with realistic costs
python liquidity_strategy.py --data wsteth_eth_data_0.01_pool_data.csv --il wsteth_eth_data_impermanent_loss.csv --tick-data wsteth_eth_data_tick_data.json --strategy adaptive --initial-capital 10000 --daily-volume 5000000 --gas-price 25 --output realistic_strategy
This performs backtesting with realistic modeling of fees, slippage, and gas costs.
# Compare multiple strategy presets
python liquidity_strategy.py --data wsteth_eth_data_0.01_pool_data.csv --initial-capital 100000 --compare-strategies --output strategy_comparison
This runs backtests on conservative, moderate, and aggressive strategy presets and provides a comparison table of results.
# Optimize tick range for specific market conditions
for range in 5 10 15 20 30; do
python liquidity_strategy.py --data wsteth_eth_data_0.01_pool_data.csv --strategy adaptive --tick-range $range --initial-capital 100000 --output range_${range}
done
Run multiple backtests with different parameters to find optimal settings.
- Optimize the RPC Alchemy calls for data collection or use the OKU API instead.
- Optimize the RPC rate limits on paid and public plans.
- Add a correct fee tier for the analysed pool in strategy simulation [now it's hardcoded as 0.001]
- Fix the fee calculation in the strategy simulation based on the correct [now it's only count the fees based on the collected tick data, that is not correct]
- Add more strategies presets and more dynamic metrics to backtest
- Implement AI Agent to automatically choose the best strategy and parameters based on the market conditions
- Improve multicall batching with more intelligent batch size adjustment
- Add support for more Uniswap V3 pools and other DEXes
- Implement position range optimization based on historical liquidity distribution
- Create a web interface for easier strategy visualization and comparison
Contributions are welcome! Please feel free to submit a Pull Request to this repository.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature
) - Commit your changes (
git commit -m 'Add some amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.