Agent skill

Lp Analyst

by ccashwell in ccashwell/evm-cortex

A skill your agent uses when analyzing LP positions, calculating impermanent loss, optimizing position ranges, estimating fee revenue, rebalancing strategies, or building automated liquidity…

MITAuto-check passedBusiness, Finance & HR

Install Lp Analyst

skills CLI
$ npx skills add ccashwell/evm-cortex --skill lp-analyst -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install ccashwell/evm-cortex lp-analyst --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/ccashwell/evm-cortex.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/lp-analyst .claude/skills/lp-analyst && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
lp-analyst
GitHub stars
131
Token cost
~5.7k tokens
SKILL.md length
1,456 words
Files
1
Skills in repo
89
Repo updated
First seen
Licence
MIT

At a glance

A skill your agent uses when analyzing LP positions, calculating impermanent loss, optimizing position ranges, estimating fee revenue, rebalancing strategies, or building automated liquidity…

  • Works in 3 steps: beforeSwap: mint concentrated liquidity… → The swap executes against this highly… → afterSwap: burn the position, collect fees
  • Analyzing LP positions
  • SKILL.md covers Impermanent Loss Mathematics, Fee Revenue Estimation, Position Range Optimization and Reading Positions Onchain, plus 3 more sections
  • Instructions only: no scripts, shell commands, URLs or credentials in SKILL.md

What it does

Lp Analyst is an agent skill from ccashwell/evm-cortex. Use when analyzing LP positions, calculating impermanent loss, optimizing position ranges, estimating fee revenue, rebalancing strategies, or building automated liquidity management. Covers both Uniswap V3 NonfungiblePositionManager and V4 PositionManager positions.

Its SKILL.md is about 5.7k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

It sits in Business, Finance & HR. It works with Uniswap and Circle USDC. The repository describes itself as: Ethereum protocol engineering squad for AI coding assistants. The licence is MIT.

When your agent uses it

  • Analyzing LP positions
  • Calculating impermanent loss
  • Optimizing position ranges
  • Estimating fee revenue

Example prompts

  • “/lp-analyst”

Workflow steps

3 steps, taken from the first numbered list in SKILL.md.

  1. beforeSwap: mint concentrated liquidity at the current tick ± 1 tick spacing
  2. The swap executes against this highly concentrated liquidity
  3. afterSwap: burn the position, collect fees

What it can do on your machine

Read from SKILL.md and the folder at commit f8f3301. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    No scripts in the folder and no shell commands in SKILL.md (its code samples are solidity and graphql).

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Lp Analyst loads about 5.7k tokens when it runs. Until then it costs about 69 tokens; SKILL.md has 1,456 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~69
When it runs · the whole SKILL.md, loaded when a task matches
~5.7k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from ccashwell/evm-cortex at commit f8f3301, republished under its MIT licence (© ccashwell). 1,456 words, ~5,743 tokens.

Download SKILL.mdSave it as .claude/skills/lp-analyst/SKILL.md (or your agent's skills folder).
name
lp-analyst
description
Use when analyzing LP positions, calculating impermanent loss, optimizing position ranges, estimating fee revenue, rebalancing strategies, or building automated liquidity management. Covers both Uniswap V3 NonfungiblePositionManager and V4 PositionManager positions.

LP Position Analysis

Impermanent Loss Mathematics

V2 Full-Range IL

For a 50/50 constant-product pool where r = current_price / entry_price:

IL(r) = 2√r / (1 + r) - 1
Price ChangerIL
-50%0.50-5.72%
-25%0.75-1.03%
-10%0.90-0.14%
0%1.000.00%
+10%1.10-0.14%
+25%1.25-0.62%
+50%1.50-2.02%
+100%2.00-5.72%
+300%4.00-20.00%

IL is always non-positive. The loss is symmetric on a log scale: a 2x and a 0.5x move produce the same IL.

V3 Concentrated Liquidity IL

For a position with tick range [tickLower, tickUpper] mapping to price range [Pa, Pb] where Pa < Pb, with liquidity L and current price P:

Position token amounts (the core V3 math):

If Pa ≤ P ≤ Pb (in range):
  amount0 = L × (1/√P - 1/√Pb)
  amount1 = L × (√P - √Pa)

If P < Pa (below range — 100% token0):
  amount0 = L × (1/√Pa - 1/√Pb)
  amount1 = 0

If P > Pb (above range — 100% token1):
  amount0 = 0
  amount1 = L × (√Pb - √Pa)

Position value in token1 terms:

value(P) = amount0 × P + amount1

Expanding for the in-range case:

value(P) = L × (P/√P - P/√Pb + √P - √Pa)
         = L × (√P - √Pa + √P - P/√Pb)
         = L × (2√P - √Pa - P/√Pb)

HODL value (holding the initial token amounts without providing liquidity):

At entry price P₀ (in range), the initial amounts are:

a0 = L × (1/√P₀ - 1/√Pb)
a1 = L × (√P₀ - √Pa)

HODL value at current price P:

value_HODL(P) = a0 × P + a1
              = L × (P/√P₀ - P/√Pb + √P₀ - √Pa)

Impermanent loss:

IL = value_LP(P) / value_HODL(P) - 1

Concentrated positions amplify IL relative to V2. The amplification factor equals the capital efficiency multiplier.

Worked Example: ETH/USDC

Setup:

  • Entry price: P₀ = 3000 USDC/ETH
  • Range: Pa = 2500, Pb = 3500
  • Deposit: 1 ETH + 3000 USDC worth of value

Compute initial amounts (P₀ = 3000, in range):

√3000 ≈ 54.772
√2500 = 50.000
√3500 ≈ 59.161

Liquidity L from 1 ETH + equivalent USDC:
  From token0: L = amount0 / (1/√P - 1/√Pb)
  From token1: L = amount1 / (√P - √Pa)

Using the ratio to find L for a balanced deposit of value V at price P₀:
  V = L × (2√P₀ - √Pa - P₀/√Pb)
  V = L × (2 × 54.772 - 50.000 - 3000/59.161)
  V = L × (109.544 - 50.000 - 50.709)
  V = L × 8.835

For V = 6000 USDC (1 ETH at $3000 + 3000 USDC):
  L = 6000 / 8.835 ≈ 679.0

Now ETH moves to $3500 (upper bound):

P = 3500 = Pb → position is 100% USDC (token1)

amount0 = 0
amount1 = L × (√Pb - √Pa) = 679.0 × (59.161 - 50.000) = 679.0 × 9.161 ≈ 6220

value_LP = 6220 USDC

HODL value at $3500:

Initial amounts at P₀ = 3000:
  a0 = 679.0 × (1/54.772 - 1/59.161) = 679.0 × (0.01826 - 0.01690) = 679.0 × 0.001359 ≈ 0.923 ETH
  a1 = 679.0 × (54.772 - 50.000) = 679.0 × 4.772 ≈ 3240 USDC

value_HODL = 0.923 × 3500 + 3240 = 3230 + 3240 = 6470 USDC

Impermanent loss:

IL = 6220 / 6470 - 1 ≈ -3.86%

Compare with V2 full-range IL at the same price move (r = 3500/3000 ≈ 1.167):

IL_v2 = 2√1.167 / (1 + 1.167) - 1 = 2 × 1.0801 / 2.167 - 1 ≈ -0.28%

The concentrated position suffers ~13-14x more IL on this move — more than the ~6.5x capital-efficiency multiplier for the 2500-3500 range. IL amplification is not a constant factor: it grows as price approaches a range edge, so the multiplier understates downside near the bounds.

Fee Revenue Estimation

Fee Accrual Model
fee_revenue = volume_in_range × fee_tier × (1 - protocol_share) × (position_liquidity / total_liquidity_in_range)

protocol_share is no longer zero. Since the UNIfication proposal executed on 2025-12-28, Ethereum mainnet V3 pools set slot0().feeProtocol to 68 on the 0.01%/0.05% tiers (protocol takes 1/4 of LP fees) and 102 on the 0.30% tier (1/6) — decode as token0 = feeProtocol % 16, token1 = feeProtocol >> 4, share = 1/N. V4 static-fee pools have charged a protocol fee since governance activated it on 2026-07-27 (mainnet ETH/USDC 500/10: protocolFee = 512125, i.e. 125 pips per direction); it is taken from the swap input ahead of the LP fee, so for V4 protocol_share ≈ protocolFee / 1e6 per direction.

Annualized:

annual_fees = daily_fee_revenue × 365
fee_APR = annual_fees / position_value

The net return of an LP position is:

net_return = fee_APR + IL

A position is profitable when fee revenue exceeds IL.

Fee Tiers
TierFeeTick SpacingTypical Pairs
0.01%1001Stablecoin/stablecoin (USDC/USDT)
0.05%50010Correlated assets (wstETH/ETH)
0.30%300060Standard pairs (ETH/USDC)
1.00%10000200Exotic / long-tail pairs
Active Time Ratio

Concentrated positions only earn fees while the current price is within range. The active time ratio α represents the fraction of time the position is in range:

effective_fee_APR = fee_APR × α

For a ±10% range on ETH/USDC, historical α is typically 60-80% over a month. Narrower ranges have lower α.

Fee Growth Tracking (V3)

Uniswap V3 tracks cumulative fees per unit of liquidity using Q128.128 fixed-point accumulators:

feeGrowthGlobal0X128  — cumulative token0 fees per unit liquidity (pool-wide)
feeGrowthGlobal1X128  — cumulative token1 fees per unit liquidity (pool-wide)

Per-position uncollected fees:

uncollected0 = (feeGrowthInside0CurrentX128 - feeGrowthInside0LastX128) × liquidity / 2^128
uncollected1 = (feeGrowthInside1CurrentX128 - feeGrowthInside1LastX128) × liquidity / 2^128

Where feeGrowthInsideX128 is computed from the tick-level feeGrowthOutside values:

solidity
// Pseudocode for feeGrowthInside
if currentTick >= tickUpper:
    feeGrowthInside = feeGrowthOutside[tickUpper] - feeGrowthOutside[tickLower]
elif currentTick < tickLower:
    feeGrowthInside = feeGrowthOutside[tickLower] - feeGrowthOutside[tickUpper]
else:
    feeGrowthInside = feeGrowthGlobal - feeGrowthOutside[tickLower] - feeGrowthOutside[tickUpper]

Position Range Optimization

Capital Efficiency Multiplier

For a range [Pa, Pb], capital efficiency relative to full range is:

efficiency = 1 / (1 - √(Pa / Pb))
RangePa/PbEfficiency
±0.1% (stables)0.998~1000x
±1%0.980~100x
±5%0.905~20.5x
±10%0.818~10.5x
±25%0.600~4.4x
±50%0.333~2.4x
Full range0→∞1x

Higher efficiency means more fees earned per dollar of capital, but also more IL per dollar and more frequent out-of-range events.

Range Selection Heuristics

Stablecoin pairs (USDC/USDT):

  • Range: ±0.05% to ±0.5% around peg
  • Fee tier: 0.01%
  • Rebalance: rarely needed if peg holds
  • Capital efficiency: 200x–2000x

Correlated pairs (wstETH/ETH):

  • Range: ±1% to ±5%
  • Fee tier: 0.05%
  • Rebalance: weekly or when staking rate changes materially
  • Capital efficiency: 20x–100x

Major pairs (ETH/USDC):

  • Range: ±10% to ±30% based on volatility regime
  • Fee tier: 0.30%
  • Rebalance: when price approaches range boundary
  • Capital efficiency: 3x–10x

Volatile pairs (memecoins, new tokens):

  • Range: ±50% or wider
  • Fee tier: 1.00%
  • Rebalance: avoid — gas often exceeds benefit
  • Capital efficiency: 1.5x–3x
Tick Math

Prices map to ticks via:

tick = floor(log(price) / log(1.0001))
price = 1.0001^tick

Tick spacing constrains which ticks can be used. A position's range must align to the pool's tick spacing:

tickLower = floor(desired_tick / tickSpacing) × tickSpacing
tickUpper = ceil(desired_tick / tickSpacing) × tickSpacing

Reading Positions Onchain

V3 NonfungiblePositionManager
solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {INonfungiblePositionManager} from "@uniswap/v3-periphery/contracts/interfaces/INonfungiblePositionManager.sol";

interface IPositionReader {
    function analyzePosition(uint256 tokenId) external view returns (
        address token0,
        address token1,
        uint24 fee,
        int24 tickLower,
        int24 tickUpper,
        uint128 liquidity
    );
}

contract V3PositionReader {
    INonfungiblePositionManager public immutable NPM;

    constructor(address npm_) {
        NPM = INonfungiblePositionManager(npm_);
    }

    function getPosition(uint256 tokenId) external view returns (
        address token0,
        address token1,
        uint24 fee,
        int24 tickLower,
        int24 tickUpper,
        uint128 liquidity,
        uint256 feeGrowthInside0LastX128,
        uint256 feeGrowthInside1LastX128,
        uint128 tokensOwed0,
        uint128 tokensOwed1
    ) {
        (
            ,              // nonce
            ,              // operator
            token0,
            token1,
            fee,
            tickLower,
            tickUpper,
            liquidity,
            feeGrowthInside0LastX128,
            feeGrowthInside1LastX128,
            tokensOwed0,
            tokensOwed1
        ) = NPM.positions(tokenId);
    }
}
V4 PositionManager
solidity
import {IPositionManager} from "v4-periphery/src/interfaces/IPositionManager.sol";
import {PoolKey} from "v4-core/src/types/PoolKey.sol";
import {PositionInfo} from "v4-periphery/src/libraries/PositionInfoLibrary.sol";

contract V4PositionReader {
    IPositionManager public immutable PM;

    constructor(address pm_) {
        PM = IPositionManager(pm_);
    }

    /// @notice Reads a V4 LP position's pool key, tick range, and liquidity
    function getPosition(uint256 tokenId) external view returns (
        PoolKey memory poolKey,
        int24 tickLower,
        int24 tickUpper,
        uint128 liquidity
    ) {
        PositionInfo info;
        (poolKey, info) = PM.getPoolAndPositionInfo(tokenId);
        tickLower = info.tickLower();
        tickUpper = info.tickUpper();
        liquidity = PM.getPositionLiquidity(tokenId);
    }
}
Converting Ticks to Prices
solidity
import {TickMath} from "v4-core/src/libraries/TickMath.sol";

/// @notice Convert a tick to a human-readable price
/// @dev price = 1.0001^tick, adjusted for token decimals
function tickToPrice(int24 tick, uint8 decimals0, uint8 decimals1) pure returns (uint256) {
    uint160 sqrtPriceX96 = TickMath.getSqrtPriceAtTick(tick);
    // price = (sqrtPriceX96 / 2^96)^2 × 10^(decimals0 - decimals1)
    uint256 priceX192 = uint256(sqrtPriceX96) * uint256(sqrtPriceX96);
    return priceX192 * (10 ** decimals0) / (10 ** decimals1) >> 192;
}

Position Value Calculation

Token Amounts from Liquidity
solidity
import {LiquidityAmounts} from "v4-periphery/src/libraries/LiquidityAmounts.sol";
import {TickMath} from "v4-core/src/libraries/TickMath.sol";

/// @notice Calculate the token amounts for a position
function getAmounts(
    int24 tickCurrent,
    int24 tickLower,
    int24 tickUpper,
    uint128 liquidity
) pure returns (uint256 amount0, uint256 amount1) {
    uint160 sqrtPriceCurrent = TickMath.getSqrtPriceAtTick(tickCurrent);
    uint160 sqrtPriceLower = TickMath.getSqrtPriceAtTick(tickLower);
    uint160 sqrtPriceUpper = TickMath.getSqrtPriceAtTick(tickUpper);

    (amount0, amount1) = LiquidityAmounts.getAmountsForLiquidity(
        sqrtPriceCurrent,
        sqrtPriceLower,
        sqrtPriceUpper,
        liquidity
    );
}
Value in USD Terms
value_usd = amount0 × price0_usd + amount1 × price1_usd

For ETH/USDC where token0 = USDC, token1 = WETH:

value_usd = amount0 × 1.0 + amount1 × eth_price_usd

Always check token ordering — V3/V4 enforce token0 < token1 by address sort.

Fee Collection

V3 Fee Collection

The NonfungiblePositionManager accumulates fees internally. To collect, first poke the position to update fee accounting, then call collect:

solidity
/// @notice Collect all accrued fees from a V3 position
/// @dev A zero-liquidity decrease pokes the position to update fee snapshots
function collectFees(
    INonfungiblePositionManager npm,
    uint256 tokenId
) external returns (uint256 collected0, uint256 collected1) {
    npm.decreaseLiquidity(INonfungiblePositionManager.DecreaseLiquidityParams({
        tokenId: tokenId,
        liquidity: 0,
        amount0Min: 0,
        amount1Min: 0,
        deadline: block.timestamp
    }));

    (collected0, collected1) = npm.collect(INonfungiblePositionManager.CollectParams({
        tokenId: tokenId,
        recipient: msg.sender,
        amount0Max: type(uint128).max,
        amount1Max: type(uint128).max
    }));
}
V4 Fee Collection

V4 has no collect. Encode [Actions.DECREASE_LIQUIDITY (liquidity = 0), Actions.TAKE_PAIR] in modifyLiquidities; the zero-liquidity decrease pokes the position and its feesAccrued are taken via TAKE_PAIR (or CLOSE_CURRENCY). Fees settle through the PoolManager's transient accounting.

Rebalancing Strategies

Time-Based

Rebalance at fixed intervals (e.g., every 24 hours, every 7 days). Simple to implement with Chainlink Automation or Gelato tasks.

Pros: predictable gas spend, simple logic Cons: rebalances even when unnecessary, misses urgent rebalances when price moves fast

Price-Based (Threshold Trigger)

Rebalance when price exits the current range or approaches a boundary within a configurable buffer:

trigger_lower = Pa + buffer
trigger_upper = Pb - buffer

When P < trigger_lower or P > trigger_upper, close the position and re-open centered at the current price.

Pros: responsive to market conditions, avoids unnecessary rebalances Cons: can trigger excessively during high volatility

IL-Threshold

Monitor unrealized IL and rebalance when it exceeds a target percentage:

if |IL| > threshold:
    rebalance()

Typical thresholds: 1-3% for stablecoin pairs, 5-10% for major pairs.

Show full SKILL.md (585 more words)Show less
Geometric Mean Centering

Center the position at the geometric mean of recent prices to minimize expected IL:

P_center = exp(mean(ln(P_1), ln(P_2), ..., ln(P_n)))
Pa = P_center / k
Pb = P_center × k

Where k is the range multiplier (e.g., k = 1.1 for a ±10% range).

Bollinger Band Range

Set the range dynamically based on historical volatility:

μ = SMA(price, window)
σ = StdDev(price, window)

Pa = μ - k × σ
Pb = μ + k × σ

With k = 2 (95% confidence), the position captures most price action. Wider k means less rebalancing but lower capital efficiency.

Rebalance Cost-Benefit Analysis

A rebalance is only worth executing if the expected gain exceeds costs:

expected_benefit = additional_fee_revenue + avoided_IL
cost = gas_cost + swap_slippage + swap_fees + position_entry_spread

rebalance if: expected_benefit > cost

On L2s (Arbitrum, Base, Optimism) gas costs are negligible, making more frequent rebalances viable. On mainnet with gas at <1 gwei (2026), rebalancing is also cheaper than historically but still requires slippage/fee accounting.

Automated Liquidity Management

Keeper-Based Rebalancing
solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {AutomationCompatibleInterface} from
    "@chainlink/contracts/src/v0.8/automation/interfaces/AutomationCompatibleInterface.sol";

/// @title LP position rebalancer using Chainlink Automation
/// @notice Monitors a V3 position and rebalances when price exits range
abstract contract LPKeeper is AutomationCompatibleInterface {
    uint256 public positionTokenId;
    uint256 public bufferBps;

    function checkUpkeep(bytes calldata)
        external
        view
        override
        returns (bool upkeepNeeded, bytes memory performData)
    {
        (int24 tickLower, int24 tickUpper, int24 tickCurrent) = _getPositionTicks();

        int24 rangeTicks = tickUpper - tickLower;
        int24 buffer = int24(int256(rangeTicks) * int256(uint256(bufferBps)) / 10_000);

        upkeepNeeded = tickCurrent <= tickLower + buffer || tickCurrent >= tickUpper - buffer;
        performData = abi.encode(tickCurrent);
    }

    function performUpkeep(bytes calldata performData) external override {
        int24 tickCurrent = abi.decode(performData, (int24));
        _rebalanceAroundTick(tickCurrent);
    }

    function _getPositionTicks() internal view virtual returns (int24, int24, int24);
    function _rebalanceAroundTick(int24 tick) internal virtual;
}
V4 Auto-Compound Hook

A V4 hook can auto-compound fees into the position on every swap:

solidity
// SwapParams lives in v4-core/src/types/PoolOperation.sol. BaseHook (OpenZeppelin uniswap-hooks or
// Uniswap/v4-hooks-public) keeps the external callbacks non-virtual; override the internal _afterSwap.
function _afterSwap(
    address,
    PoolKey calldata key,
    SwapParams calldata,
    BalanceDelta delta,
    bytes calldata
) internal override returns (bytes4, int128) {
    // Collect fees generated by the swap and re-add as liquidity
    // This is possible because V4 hooks execute within the PoolManager's
    // unlock context, allowing atomic fee collection + liquidity addition
    return (BaseHook.afterSwap.selector, 0);
}
JIT (Just-In-Time) Liquidity

JIT liquidity adds a large concentrated position immediately before a swap and removes it after, capturing the swap fee on a narrow range. In V4, this can be implemented as a hook:

  1. beforeSwap: mint concentrated liquidity at the current tick ± 1 tick spacing
  2. The swap executes against this highly concentrated liquidity
  3. afterSwap: burn the position, collect fees

JIT liquidity is MEV-adjacent — it competes with other LPs for fee revenue without taking price risk.

ERC-4626 LP Vault

Wrap Uniswap LP positions in an ERC-4626 vault for composability:

solidity
/// @title Uniswap V3 LP Vault
/// @notice ERC-4626 vault that manages a concentrated liquidity position
/// @dev Depositors receive vault shares proportional to their contribution
abstract contract LPVault {
    // vault share accounting
    // deposit: add liquidity to position, mint shares
    // withdraw: remove liquidity proportionally, burn shares
    // compound: collect fees, re-add as liquidity
    // rebalance: close position, re-open at new range
}

Key considerations:

  • Share pricing must account for both token amounts and uncollected fees
  • Rebalance triggers need governance or keeper access control
  • Slippage protection on all swaps during rebalance
  • Vault tokens should be non-rebasing (track shares, not token amounts)

Analytics Queries

Subgraph: Position Performance
graphql
{
  position(id: "tokenId") {
    id
    owner
    liquidity
    depositedToken0
    depositedToken1
    withdrawnToken0
    withdrawnToken1
    collectedFeesToken0
    collectedFeesToken1
    pool {
      token0 { symbol decimals }
      token1 { symbol decimals }
      feeTier
      sqrtPrice
      tick
    }
    tickLower { tickIdx }
    tickUpper { tickIdx }
  }
}
Subgraph: Pool Volume by Tick Range
graphql
{
  poolDayDatas(
    where: { pool: "poolAddress" }
    orderBy: date
    orderDirection: desc
    first: 30
  ) {
    date
    volumeUSD
    tvlUSD
    feesUSD
    tick
  }
}
Fee APR Calculation from Subgraph Data
daily_fees_usd = sum(feesUSD over 24h for ticks in [tickLower, tickUpper])
position_share = position_liquidity / total_liquidity_in_tick_range
my_daily_fees = daily_fees_usd × (1 - protocol_share) × position_share   # subgraph feesUSD is gross of the protocol fee
fee_APR = (my_daily_fees / position_value_usd) × 365
Volume-to-Liquidity Ratio

Higher V/L ratio means more fee revenue per unit of liquidity:

VL_ratio = volume_24h / TVL_in_range
expected_daily_yield = VL_ratio × fee_tier × (1 - protocol_share)

Risk Metrics

IL Sensitivity

Measure how IL changes per 1% price move:

IL_sensitivity = dIL/dr at r = 1

For V2: dIL/dr = (1 - √r) / (1 + r)^2
At r = 1: dIL/dr = 0 (IL is locally flat at entry)

Second derivative: d²IL/dr² = -(3 + r) / (4√r × (1 + r)^3)
At r = 1: d²IL/dr² = -1/4

IL ≈ -(Δr)² / 8 for small moves

For concentrated positions, multiply by the capital efficiency factor.

Breakeven Fee APR

The minimum fee APR needed to offset IL over a given period:

breakeven_APR = -IL / holding_period_in_years

For the ETH/USDC example above (IL = -3.86% when ETH goes from $3000 to $3500):

If the move happened over 30 days:
breakeven_APR = 0.0386 / (30/365) ≈ 46.97%
Maximum Drawdown

For a concentrated position [Pa, Pb] entered at price P₀:

If price crashes to Pa:
  max_drawdown_below = value(Pa) / value(P₀) - 1

If price spikes to Pb:
  max_drawdown_above = value(Pb) / value(P₀) - 1

Out-of-range positions experience the worst-case: the position becomes 100% of the depreciating token (below range) or 100% of the appreciating token you no longer hold (above range).

Volatility-IL Relationship

For a token with annualized volatility σ and a holding period t (in years):

Expected IL (V2) ≈ -σ²t / 8

Derivation: price follows geometric Brownian motion, ln(r) is normally distributed with variance σ²t, and the second-order Taylor expansion of IL gives the -σ²/8 coefficient.

For concentrated positions with efficiency multiplier E:

Expected IL (V3) ≈ -E × σ²t / 8
Position Duration vs Profitability

Longer positions in range accumulate more fees. The crossover point where fees exceed IL depends on:

t_breakeven = |IL| / fee_rate_per_unit_time

Empirically for ETH/USDC 0.30% tier with ±10% range:

  • < 7 days: fees unlikely to offset IL during volatile periods
  • 7-30 days: breakeven zone
  • 30 days: fees typically dominate if position stays in range

Checklist

  • Verify token ordering (token0 < token1 by address) before interpreting amounts
  • Account for decimal differences when computing USD values (USDC = 6, WETH = 18)
  • Use SafeERC20 for all token transfers in LP management contracts
  • Include slippage protection (amount0Min, amount1Min) on all liquidity operations
  • Track fee growth snapshots (feeGrowthInside0LastX128) for accurate fee accounting
  • Validate tick alignment to pool's tick spacing before creating positions
  • Compare rebalance cost (gas + slippage + fees) against expected benefit before executing
  • Test with forked mainnet to verify against real pool state and liquidity depth
  • Handle edge cases: zero liquidity, position fully out of range, pool not initialized
  • For V4: ensure all position modifications happen within PoolManager.unlock() context

© ccashwell, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in skills/lp-analyst of ccashwell/evm-cortex.

Open the folder on GitHubat commit f8f3301

Compare with similar skills

Lp Analyst next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Lp Analyst compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Lp Analyst this skillccashwell/evm-cortex131—~5.7kAutomated safety check: PassMIT
Addressesaustintgriffith/ethskills294—~7.1kAutomated safety check: PassNone
Aomi Transactjeremylongshore/tons-of-skills-marketplace2.8k—~2.3kAutomated safety check: PassMIT
Setup DcaLeoYeAI/openclaw-master-skills2.2k—~4.3kAutomated safety check: PassMIT
Aero Stock LpBankrBot/skills1.2k—~5.8kAutomated safety check: PassNone
Okx Dapp Discoveryinternet-court/internet-court-skill6.4k2 repos~6.9kAutomated safety check: PassMIT

Similar skills

  • Addresses

    austintgriffith/ethskills

    Verified contract addresses for major Ethereum protocols across mainnet and L2s.

    294 GitHub stars~7.1k tokensUpdated 1 mo ago
    Business, Finance & HRAuto-check passed
  • Aomi Transact

    jeremylongshore/tons-of-skills-marketplace

    Build natural-language crypto agents, web3 assistants, and trading bots that read and write EVM chain state.

    2.8k GitHub stars~2.3k tokensUpdated today
    Business, Finance & HRAuto-check passed
  • Setup Dca

    LeoYeAI/openclaw-master-skills

    Set up a non-custodial dollar-cost averaging strategy on Uniswap.

    2.2k GitHub stars~4.3k tokensUpdated 2 mo ago
    Business, Finance & HRAuto-check passed
  • Aero Stock Lp

    BankrBot/skills

    LP tokenized stocks onchain — range-LP Coinbase tokenized equities (NVDA, AAPL, GOOGL, META) and AERO/USDC on Aerodrome Slipstream (Base) for trading-fee + AERO emission yield.

    1.2k GitHub stars~5.8k tokensUpdated 3 days ago
    Business, Finance & HRAuto-check passed
  • Okx Dapp Discovery

    internet-court/internet-court-skill

    Plugin router for 20 third-party DeFi protocols (Polymarket, Aave, Hyperliquid, PancakeSwap, Morpho, Raydium, Curve, Compound, Pendle, Lido, ether.fi, GMX, Kamino, Orca, Meteora, Clanker, pump.fun…

    6.4k GitHub starsUsed in 2 repos~6.9k tokens
    Backend & APIsAuto-check passed
  • Trading Evm

    alsk1992/CloddsBot

    Trade tokens on EVM chains - Uniswap V3, 1inch on Ethereum, Arbitrum, Optimism, Base, Polygon

    2.9k GitHub stars~1.9k tokensUpdated 5 days ago
    Backend & APIsAuto-check passed

More from ccashwell/evm-cortex

All 89 skills in this repo
  • Xray Pre Audit

    ccashwell/evm-cortex

    A skill your agent uses when preparing for a security audit, performing reconnaissance on a new codebase, or creating a protocol overview.

    131 GitHub stars~25k tokensUpdated 8 days ago
    Auto-check passed
  • Aave Integration

    ccashwell/evm-cortex

    A skill your agent uses when integrating with Aave V3 for lending, borrowing, flash loans, or building on top of Aave markets.

    131 GitHub stars~1.3k tokensUpdated 8 days ago
    Auto-check passed
  • Access Control Patterns

    ccashwell/evm-cortex

    Access control design patterns for Solidity protocols. An agent skill from ccashwell/evm-cortex.

    131 GitHub stars~1.8k tokensUpdated 8 days ago
    Auto-check passed
  • Anvil Patterns

    ccashwell/evm-cortex

    A skill your agent uses when running a local Ethereum node with Anvil.

    131 GitHub stars~1.3k tokensUpdated 8 days ago
    Auto-check passed
  • Audit Breadth Scan

    ccashwell/evm-cortex

    A skill your agent uses when performing systematic breadth-first review of all contracts during a security audit.

    131 GitHub stars~1.4k tokensUpdated 8 days ago
    Auto-check passed
  • Audit Depth Analysis

    ccashwell/evm-cortex

    A skill your agent uses when performing deep analysis of specific findings or high-risk areas during a security audit.

    131 GitHub stars~1.6k tokensUpdated 8 days ago
    Auto-check passed

Questions about Lp Analyst

What does Lp Analyst do?

A skill your agent uses when analyzing LP positions, calculating impermanent loss, optimizing position ranges, estimating fee revenue, rebalancing strategies, or building automated liquidity…. Lp Analyst is an agent skill from ccashwell/evm-cortex. Use when analyzing LP positions, calculating impermanent loss, optimizing position ranges, estimating fee revenue, rebalancing strategies, or building automated liquidity management.

When should I use Lp Analyst?

Lp Analyst fits situations like: analyzing LP positions; calculating impermanent loss; optimizing position ranges; estimating fee revenue.

How do I install Lp Analyst in Claude Code?

Run `npx skills add ccashwell/evm-cortex --skill lp-analyst -a claude-code`. Or copy the skill folder (skills/lp-analyst in ccashwell/evm-cortex) into .claude/skills/lp-analyst in your project. Claude Code loads it when a task matches its description.

How do I install Lp Analyst in Codex?

Run `npx skills add ccashwell/evm-cortex --skill lp-analyst -a codex`. Or copy the skill folder (skills/lp-analyst in ccashwell/evm-cortex) into .agents/skills/lp-analyst in your project. Codex loads it when a task matches its description.

Can I use Lp Analyst in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add ccashwell/evm-cortex --skill lp-analyst -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/lp-analyst, .gemini/skills/lp-analyst, .github/skills/lp-analyst and .opencode/skills/lp-analyst in your project.

What does Lp Analyst need to run?

SKILL.md names no scripts, command-line tools or credentials: Lp Analyst is instructions for the agent only.

Does Lp Analyst access the network?

SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.

Is Lp Analyst safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Lp Analyst use?

Lp Analyst is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Lp Analyst use?

About 5.7k tokens (SKILL.md is roughly 23k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Lp Analyst?

Skills that share tags, products or a category with Lp Analyst: Addresses (austintgriffith/ethskills, 294 stars), Aomi Transact (jeremylongshore/tons-of-skills-marketplace, 2.8k stars), Setup Dca (LeoYeAI/openclaw-master-skills, 2.2k stars) and Aero Stock Lp (BankrBot/skills, 1.2k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Lp Analyst?

ccashwell (a GitHub user) maintains it in ccashwell/evm-cortex, which has 131 GitHub stars. The repository holds 89 skills in this directory. The repository was last updated on September 30, 2026.

Source: ccashwell/evm-cortex on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.