Skip to content

Price Scenarios

Price Scenarios primitives project an LP position’s value at a hypothetical price change from the current pool state. Three primitives, one per protocol family: Uniswap V2/V3, Balancer (2-asset weighted), Stableswap (2-asset).

These are paper projections — no settlement-swap impact. Use them to answer “if ETH drops 30% from here, what’s my position worth?”

All primitives in the Agentic Primitives section follow the same contract: stateless construction, computation at .apply(), typed dataclass return.

from defipy.twin import MockProvider, StateTwinBuilder
provider = MockProvider()
builder = StateTwinBuilder()
lp_v2 = builder.build(provider.snapshot("eth_dai_v2"))
lp_bal = builder.build(provider.snapshot("eth_dai_balancer_50_50"))
lp_sts = builder.build(provider.snapshot("usdc_dai_stableswap_A10"))

Purpose. Project a Uniswap V2/V3 LP position’s value at a hypothetical price change.

Signature.

SimulatePriceMove().apply(
lp, price_change_pct, position_size_lp,
lwr_tick=None, upr_tick=None,
) -> PriceMoveScenario

price_change_pct is a fraction (-0.30 = 30% drop), bounded by > -1.0. position_size_lp is in LP-token units; the result is scale-invariant in this size, so use any positive value. fee_projection is always None in v1 (no fee modeling). Numeraire is token0.

from defipy import SimulatePriceMove
# What happens to a 10,000 LP-share position if ETH drops 30%?
result = SimulatePriceMove().apply(
lp_v2,
price_change_pct = -0.30,
position_size_lp = 10000.0,
)
print(f"new_price_ratio: {result.new_price_ratio:.6f}")
print(f"new_value: {result.new_value:.4f}")
print(f"il_at_new_price: {result.il_at_new_price:.6f}")
print(f"value_change_pct: {result.value_change_pct:.6f}")
print(f"fee_projection: {result.fee_projection}")
new_price_ratio: 0.700000 new_value: 2390.4572 il_at_new_price: -0.015694 value_change_pct: 0.195229 fee_projection: None

Purpose. Same projection adapted to a 2-asset Balancer weighted pool. The IL formula uses base/opp weights (not just the price ratio).

Signature.

SimulateBalancerPriceMove().apply(lp, price_change_pct, lp_init_amt) -> BalancerPriceMoveScenario

Numeraire is opp-token units. Raises ValueError if lp is not a BalancerExchange or pool is N>2 (propagated from BalancerImpLoss).

from defipy import SimulateBalancerPriceMove
result = SimulateBalancerPriceMove().apply(
lp_bal,
price_change_pct = -0.30,
lp_init_amt = 100.0,
)
print(f"base / opp: {result.base_tkn_name} / {result.opp_tkn_name}")
print(f"base_weight: {result.base_weight}")
print(f"new_price_ratio: {result.new_price_ratio:.6f}")
print(f"new_value: {result.new_value:.4f}")
print(f"il_at_new_price: {result.il_at_new_price:.6f}")
print(f"value_change_pct: {result.value_change_pct:.6f}")
base / opp: ETH / DAI base_weight: 0.5 new_price_ratio: 0.700000 new_value: 167332.0053 il_at_new_price: -0.015694 value_change_pct: -0.163340

Purpose. Project a 2-asset stableswap LP position’s value at a hypothetical depeg. High A makes small price deviations costly.

Signature.

SimulateStableswapPriceMove().apply(lp, price_change_pct, lp_init_amt) -> StableswapPriceMoveScenario

Use small moves — stableswap is depeg-sensitive. Catches DepegUnreachableError: if the projected alpha is unreachable, new_value / il_at_new_price / value_change_pct come back as None (token_names, A, new_price_ratio still populated). At-peg short-circuit returns IL=0 exactly.

from defipy import SimulateStableswapPriceMove
# Stableswap is sensitive — model a 5% depeg.
result = SimulateStableswapPriceMove().apply(
lp_sts,
price_change_pct = -0.05,
lp_init_amt = 100.0,
)
print(f"token_names: {result.token_names}")
print(f"A: {result.A}")
print(f"new_price_ratio: {result.new_price_ratio:.6f}")
print(f"new_value: {result.new_value}")
print(f"il_at_new_price: {result.il_at_new_price}")
print(f"value_change_pct: {result.value_change_pct}")
token_names: ['USDC', 'DAI'] A: 10 new_price_ratio: 0.950000 new_value: 99.51570243887126 il_at_new_price: -0.004842975611287428 value_change_pct: -0.004842975611287414
ProtocolSupportedNotes
Uniswap V2SimulatePriceMove — closed-form V2 IL
Uniswap V3SimulatePriceMove with lwr_tick/upr_tick for range-aware IL
BalancerSimulateBalancerPriceMove — 2-asset only
StableswapSimulateStableswapPriceMove — 2-asset only; unreachable-alpha returns None for value fields

All three primitives are surfaced as MCP tools in the curated set of 10:

  • SimulatePriceMove
  • SimulateBalancerPriceMove
  • SimulateStableswapPriceMove

Price-scenario questions (“what if ETH drops?”) are highest-traffic agent queries, so all three protocol variants are exposed directly rather than left to the LLM to compose.