
Slippage Modeling
- 206 installs
- 257 repo stars
- Updated June 24, 2026
- agiprolabs/claude-trading-skills
slippage-modeling is a Claude Code skill for estimating DEX execution costs, modeling slippage curves from AMM liquidity depth, and computing optimal trade sizes.
About
slippage-modeling is a Claude Code skill for estimating execution costs on decentralized exchanges. It derives slippage from AMM bonding-curve mechanics, models CLMM concentrated liquidity, measures empirical slippage from Jupiter quotes, and combines price impact, DEX fees, priority fees, and MEV into a total cost model. A developer uses it to size trades within a slippage threshold and compute break-even thresholds before executing.
- Constant-product and CLMM slippage formulas with worked tables
- Total execution cost model: price impact + fees + priority + MEV
- Optimal trade sizing and multi-tranche splitting
Slippage Modeling by the numbers
- 206 all-time installs (skills.sh)
- Ranked #456 of 1,106 Finance & Trading skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
slippage-modeling capabilities & compatibility
- Capabilities
- slippage modeling · trade sizing · execution cost · mev analysis
- Use cases
- trading · data analysis
- Pricing
- Free
What slippage-modeling says it does
Slippage is the difference between the **expected price** at the time you decide to trade and the **actual execution price** you receive.
**Key insight**: Slippage scales with `trade_size / (reserves + trade_size)`.
npx skills add https://github.com/agiprolabs/claude-trading-skills --skill slippage-modelingAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 206 |
|---|---|
| repo stars | ★ 257 |
| Last updated | June 24, 2026 |
| Repository | agiprolabs/claude-trading-skills ↗ |
What it does
Estimate DEX execution cost and pick optimal trade sizes that keep slippage within a threshold.
Who is it for?
Sizing on-chain trades and computing break-even thresholds from AMM and CLMM liquidity mechanics.
Skip if: Centralized-exchange slippage, which the skill notes depends on hidden order-book dynamics rather than deterministic AMM curves.
When should I use this skill?
You need to know the true execution cost or safe trade size before placing an on-chain swap.
What you get
A per-trade execution cost estimate and a maximum trade size that keeps slippage within a chosen threshold.
- A fitted slippage curve for a token
- A total execution-cost and break-even estimate
By the numbers
- Slippage reference table for 6 trade/reserve ratios
- Empirical curve fit across 6 trade sizes (0.01-50 SOL)
- Concentration factors typically 5-50x
Files
Slippage Modeling
Estimate execution costs, model slippage curves from AMM mechanics and empirical quotes, and determine optimal trade sizes that keep costs within acceptable thresholds.
What Is Slippage?
Slippage is the difference between the expected price at the time you decide to trade and the actual execution price you receive. On decentralized exchanges, slippage is deterministic and measurable — unlike CEX slippage, which depends on hidden order book dynamics.
Example: You expect to buy a token at 0.001 SOL. Your trade executes at 0.00105 SOL. That 5% difference is slippage — it directly reduces your profit and increases your break-even threshold.
Sources of Slippage
1. AMM Price Impact (Primary Source)
Automated market makers use bonding curves that move price as liquidity is consumed. On a constant-product AMM (x * y = k):
price_impact = Δx / (x + Δx)Where x is the reserve of the input token and Δx is your trade size. A 1 SOL trade against a pool with 100 SOL reserves produces ~1% price impact. Against 10 SOL reserves, it produces ~10%.
See references/slippage_math.md for full derivations and CLMM adjustments.
2. DEX Fees
Every swap incurs a fee taken from the trade:
| DEX | Fee | Notes |
|---|---|---|
| Raydium | 0.25% | Standard AMM pools |
| Orca | 0.30% | Whirlpool concentrated pools |
| Meteora | 0.1–2.0% | Dynamic fees based on volatility |
| PumpFun | 1.0% | Bonding curve phase |
3. Priority Fees
Solana validators prioritize transactions with higher compute unit prices. During congestion or for time-sensitive trades:
- Normal: 0.0001 SOL (negligible)
- Competitive: 0.001–0.01 SOL
- High congestion: 0.01–0.1 SOL
4. MEV (Sandwich Attacks)
Searchers detect pending swaps and sandwich them — buying before your trade (raising the price) and selling after (capturing the difference). MEV cost depends on:
- Trade size (larger = more attractive target)
- Token liquidity (thin pools = easier to manipulate)
- Slippage tolerance setting (higher tolerance = more extractable)
Typical MEV cost: 0–200 bps on vulnerable trades.
5. Stale Quotes
Between receiving a quote and landing the transaction on-chain (0.4–2 seconds on Solana), the price may move. Volatile tokens can shift 50–500 bps in that window.
Constant-Product Slippage Formula
For a pool with reserves (x, y) and invariant k = x * y:
Buying tokens with SOL (input Δx SOL):
tokens_received = y * Δx / (x + Δx)
effective_price = Δx / tokens_received = (x + Δx) / y
spot_price = x / y
price_impact = effective_price / spot_price - 1 = Δx / (x + Δx)Selling tokens for SOL (input Δy tokens):
sol_received = x * Δy / (y + Δy)
effective_price = sol_received / Δy = x / (y + Δy)
spot_price = x / y
price_impact = 1 - effective_price / spot_price = Δy / (y + Δy)Key insight: Slippage scales with trade_size / (reserves + trade_size). This is approximately linear for small trades and accelerates sharply as trade size approaches reserve size.
Quick Reference Table
| Trade / Reserve Ratio | Approximate Slippage |
|---|---|
| 0.1% | 0.1% (1 bp) |
| 1% | 1.0% (100 bps) |
| 5% | 4.8% (476 bps) |
| 10% | 9.1% (909 bps) |
| 25% | 20% (2000 bps) |
| 50% | 33% (3333 bps) |
CLMM Slippage
Concentrated Liquidity Market Makers (Orca Whirlpools, Meteora DLMM) concentrate liquidity in specific price ranges:
- Within the active range: slippage is lower than constant-product by a concentration factor
- Crossing tick boundaries: additional slippage as the next tick's liquidity may be sparse
- Approximation:
clmm_slippage ≈ cp_slippage / concentration_factor
Typical concentration factors: 5–50x for well-managed positions.
Empirical Slippage Measurement
Theoretical formulas assume single-pool routing. In practice, Jupiter aggregates across multiple pools and routes. Empirical measurement is more accurate:
1. Query Jupiter /quote at multiple trade sizes (0.01, 0.1, 1, 5, 10, 50 SOL) 2. Record output amount and effective price at each size 3. Compute slippage in bps relative to smallest trade (proxy for spot) 4. Fit a power-law model: slippage_bps = a * trade_size^b
This captures real routing behavior, multi-pool splitting, and available liquidity.
See scripts/slippage_curve.py for the full implementation.
Total Execution Cost Model
total_cost_bps = price_impact_bps + fee_bps + priority_fee_bps + mev_risk_bps
total_cost_sol = trade_size_sol * total_cost_bps / 10_000See references/cost_model.md for component breakdowns and worked examples.
Break-Even Analysis
For a roundtrip (buy + sell):
roundtrip_cost_bps = entry_impact + exit_impact + 2 * fee_bps + 2 * priority_bps + mev_bpsThe token must move more than roundtrip_cost_bps in your favor to be profitable. For a token with 200 bps entry slippage, 200 bps exit slippage, and 50 bps fees:
roundtrip = 200 + 200 + 50 = 450 bps = 4.5%You need at least a 4.5% price move just to break even.
See scripts/execution_cost.py for automated cost estimation.
Optimal Trade Sizing
Maximum Size for Slippage Threshold
Given a slippage curve s(q) = a * q^b, solve for max trade size:
q_max = (threshold_bps / a) ^ (1/b)Multi-Tranche Execution
For large orders, splitting reduces total slippage because each tranche faces a partially-reset order book (on CLMMs) or allows arbitrageurs to rebalance between tranches:
n_tranches = ceil(total_size / q_max)
tranche_size = total_size / n_tranches
wait_between = 2-10 seconds (allow arb rebalancing)TWAP Strategy
Time-Weighted Average Price execution:
- Divide total order into equal-sized tranches
- Execute one tranche per interval (e.g., every 10 seconds)
- Total slippage is significantly lower than single execution
- Tradeoff: price may move against you during execution window
Slippage by Token Category
| Category | Typical Pool TVL | Slippage for 1 SOL | Slippage for 10 SOL |
|---|---|---|---|
| Blue chip | >$10M | <5 bps | <20 bps |
| Mid-cap | $100K–$10M | 10–50 bps | 50–500 bps |
| Small-cap | $10K–$100K | 50–200 bps | 500–2000 bps |
| Micro/PumpFun | <$10K | 200–2000 bps | Often impossible |
Integration Points
- liquidity-analysis: Get pool TVL and reserve data to feed slippage estimates
- position-sizing: Use max trade size from slippage curve as a position size constraint
- jupiter-api: Fetch real quotes for empirical slippage measurement
- risk-management: Include execution costs in risk/reward calculations
- dex-pool-analysis: Understand pool mechanics that drive slippage
Files
References
| File | Description |
|---|---|
references/slippage_math.md | AMM slippage derivations, CLMM adjustments, multi-pool routing math |
references/cost_model.md | Total execution cost components, break-even analysis, cost comparison tables |
Scripts
| File | Description |
|---|---|
scripts/slippage_curve.py | Build empirical slippage curves from Jupiter quotes, fit power-law model |
scripts/execution_cost.py | Estimate total execution cost and break-even for a specific trade |
Total Execution Cost Model
Component Breakdown
Every trade on Solana DEXes incurs multiple cost components. Understanding each one is critical for accurate break-even analysis and position sizing.
1. Price Impact (AMM Slippage)
The largest and most variable cost. Depends on trade size relative to pool liquidity.
| Pool TVL | 0.1 SOL | 1 SOL | 10 SOL | 50 SOL |
|---|---|---|---|---|
| $1M+ | <1 bps | 1-5 bps | 5-50 bps | 50-200 bps |
| $100K | 1-5 bps | 10-50 bps | 100-500 bps | 500+ bps |
| $10K | 5-50 bps | 50-500 bps | Often unfeasible | — |
| <$5K | 50+ bps | 500+ bps | — | — |
2. DEX Swap Fees
Fees are taken from the input amount before the swap executes.
| Protocol | Fee Rate | Basis Points | Notes |
|---|---|---|---|
| Raydium AMM | 0.25% | 25 bps | Standard constant-product |
| Raydium CLMM | 0.01-1% | 1-100 bps | Varies by pool fee tier |
| Orca Whirlpool | 0.01-1% | 1-100 bps | Fee tiers: 1, 4, 8, 64, 128 bps |
| Meteora DLMM | 0.1-2% | 10-200 bps | Dynamic fees increase with volatility |
| PumpFun | 1.0% | 100 bps | Bonding curve phase |
Jupiter typically selects the lowest-fee route automatically.
3. Solana Base Transaction Fee
- Base fee: 5000 lamports = 0.000005 SOL
- Cost in bps: For a 1 SOL trade, this is 0.0005 bps — completely negligible
- This is the only guaranteed fixed cost
4. Priority Fee (Compute Unit Price)
Required for timely inclusion during congestion or competitive trading.
| Scenario | Priority Fee | For 1 SOL Trade |
|---|---|---|
| Low congestion | 0.0001 SOL | 1 bps |
| Normal | 0.001 SOL | 10 bps |
| High congestion | 0.005-0.01 SOL | 50-100 bps |
| Extreme/sniping | 0.05-0.5 SOL | 500-5000 bps |
Priority fees are fixed per transaction, so they have higher bps impact on smaller trades.
5. MEV Cost (Sandwich Attacks)
Estimated additional cost from MEV extraction. Not guaranteed to occur, but should be budgeted for high-value trades.
Risk factors:
- Trade size > 5 SOL on thin pools: high risk
- Slippage tolerance > 5%: high risk
- Popular meme tokens during launch: very high risk
- Blue chip tokens with deep liquidity: low risk
Estimated MEV cost:
| Risk Level | Estimated Cost | When |
|---|---|---|
| Negligible | 0 bps | Small trades, deep liquidity |
| Low | 5-20 bps | Medium trades, good liquidity |
| Medium | 20-100 bps | Large trades on mid-cap tokens |
| High | 100-500 bps | Large trades on thin/hyped tokens |
Mitigation: Use Jupiter's exact-out mode, set tight slippage tolerance, use Jito bundles for MEV protection.
Total Cost Formula
total_bps = impact_bps + fee_bps + priority_bps + mev_bps
total_sol = trade_size_sol * total_bps / 10_000Example: Buy 5 SOL of a Mid-Cap Token ($500K TVL Pool)
| Component | Estimate |
|---|---|
| Price impact | 80 bps |
| DEX fee | 25 bps |
| Priority fee | 10 bps |
| MEV risk | 20 bps |
| Total | 135 bps |
| Cost in SOL | 0.0675 SOL |
Break-Even Analysis
Roundtrip Cost
A complete trade (entry + exit) incurs costs twice:
roundtrip_bps = entry_impact + exit_impact + 2 * fee_bps + 2 * priority_bps + mev_bpsNote: Exit slippage may differ from entry because:
- Pool liquidity may have changed
- Selling into a pool you bought from has the same reserves minus your tokens
- Market conditions and priority fees may differ
Conservative Roundtrip Estimates
| Token Category | Entry Cost | Exit Cost | Roundtrip | Min Move to Profit |
|---|---|---|---|---|
| Blue chip | 30 bps | 30 bps | 60 bps | 0.6% |
| Mid-cap | 135 bps | 150 bps | 285 bps | 2.85% |
| Small-cap | 300 bps | 400 bps | 700 bps | 7.0% |
| Micro/PumpFun | 800 bps | 1200 bps | 2000 bps | 20% |
Required Return Calculation
required_return = roundtrip_cost_bps / (10_000 - roundtrip_cost_bps)For 285 bps roundtrip: 285 / 9715 = 2.93% actual price move needed.
Risk-Adjusted Analysis
Factor in win rate to determine if execution costs make a strategy viable:
expected_pnl = win_rate * avg_win - (1 - win_rate) * avg_loss - roundtrip_costIf roundtrip cost is 285 bps per trade and you make 100 trades per month:
- Monthly execution cost: 28,500 bps equivalent
- With 1 SOL average position: 2.85 SOL/month in execution costs alone
When Costs Make a Trade Unprofitable
Rules of thumb for whether execution costs are prohibitive:
1. Roundtrip cost > 5% of trade value: Only trade if expecting 10%+ move 2. Slippage > expected daily range: The token cannot reasonably move enough to cover costs in your timeframe 3. Priority fee > 10% of expected profit: Reduce urgency or wait for lower congestion 4. MEV risk is "high": Use MEV protection (Jito bundles) or reduce trade size
Cost Optimization Strategies
Reduce Price Impact
- Split large orders into tranches (see SKILL.md on multi-tranche execution)
- Trade during high-liquidity hours (US market hours for Solana)
- Use limit orders when available (Jupiter DCA or limit order program)
Reduce Fees
- Jupiter automatically routes to lowest-fee pools
- Raydium CLMM 1 bps fee tier pools exist for major pairs
- Avoid PumpFun bonding curve when Raydium pool exists
Reduce Priority Fees
- Avoid trading during network congestion spikes
- Use dynamic priority fee estimation (query recent slot leaders)
- Accept slightly longer confirmation times for non-urgent trades
Reduce MEV Exposure
- Set slippage tolerance as tight as possible (1-3% for liquid tokens)
- Use Jito bundles for MEV-protected execution
- Trade smaller sizes (not worth sandwiching)
- Avoid mempool-visible transactions where possible
Slippage Math — AMM Derivations & Multi-Pool Routing
Constant-Product AMM (x * y = k)
Setup
A pool has reserves (x, y) where:
x= SOL reservesy= token reservesk = x * y= constant invariant (ignoring fees)
Spot price of token in SOL: P_spot = x / y
Buy: Swapping Δx SOL for Tokens
After depositing Δx SOL, new SOL reserves = x + Δx. To maintain invariant:
(x + Δx) * (y - Δy) = k = x * y
y - Δy = x * y / (x + Δx)
Δy = y - x * y / (x + Δx)
Δy = y * Δx / (x + Δx)Effective price (SOL per token):
P_eff = Δx / Δy = Δx * (x + Δx) / (y * Δx) = (x + Δx) / yPrice impact (fractional):
impact = P_eff / P_spot - 1
= [(x + Δx) / y] / [x / y] - 1
= (x + Δx) / x - 1
= Δx / xWait — this gives Δx / x, but the commonly cited formula is Δx / (x + Δx). The difference is reference frame:
Δx / x= price impact relative to pre-trade spot priceΔx / (x + Δx)= fraction of output lost vs. a hypothetical zero-impact trade
Output loss fraction (more practically useful):
ideal_output = y * Δx / x (at spot price, no impact)
actual_output = y * Δx / (x + Δx)
loss_fraction = 1 - actual/ideal = Δx / (x + Δx)This Δx / (x + Δx) is what traders experience as "slippage."
Worked Example
Pool: 1000 SOL / 10,000,000 tokens (spot price = 0.0001 SOL/token)
Trade: Buy with 10 SOL
Δy = 10,000,000 * 10 / (1000 + 10) = 99,009.90 tokens
Ideal output = 10,000,000 * 10 / 1000 = 100,000 tokens
Slippage = 1 - 99,009.90 / 100,000 = 0.99% ≈ 99 bps
Check: Δx/(x+Δx) = 10/1010 = 0.99% ✓Trade: Buy with 100 SOL
Δy = 10,000,000 * 100 / (1000 + 100) = 909,090.9 tokens
Ideal output = 1,000,000 tokens
Slippage = 1 - 909,090.9 / 1,000,000 = 9.09% ≈ 909 bps
Check: 100/1100 = 9.09% ✓Sell: Swapping Δy Tokens for SOL
Depositing Δy tokens, new token reserves = y + Δy:
(x - Δx_out) * (y + Δy) = k = x * y
Δx_out = x * Δy / (y + Δy)Output loss fraction:
ideal_sol = x * Δy / y
actual_sol = x * Δy / (y + Δy)
loss_fraction = Δy / (y + Δy)Slippage Asymmetry
Buying and selling the same dollar amount produce different slippage because they draw from different reserves. If SOL reserves are 1000 and token reserves are 10M:
- Buying 10 SOL worth: slippage = 10/1010 = 0.99%
- Selling 100,000 tokens (worth ~10 SOL): slippage = 100,000/10,100,000 = 0.99%
The slippage is symmetric in value terms for constant-product. But if reserves are imbalanced in value (price moved from initial), the side with less value has higher slippage per dollar.
Fee-Adjusted Slippage
Most AMMs take fees before the swap. For a fee rate f (e.g., 0.0025 for 0.25%):
effective_input = Δx * (1 - f)
Δy = y * effective_input / (x + effective_input)
total_slippage = 1 - Δy / ideal_output
= 1 - (1-f) * x / (x + Δx*(1-f))For small trades, total slippage ≈ f + Δx/x (fee plus impact).
Concentrated Liquidity (CLMM)
How Concentration Affects Slippage
In CLMMs (Orca Whirlpools, Meteora DLMM), liquidity providers specify a price range [P_low, P_high]. Within this range, the effective reserves are amplified:
effective_x = real_x * concentration_factor
concentration_factor ≈ 1 / (1 - sqrt(P_low/P_high))For a position spanning ±5% around current price:
concentration ≈ 1 / (1 - sqrt(0.95/1.05)) ≈ 1 / (1 - 0.951) ≈ 20xSlippage within active range:
clmm_slippage ≈ cp_slippage / concentration_factorA trade that would cause 100 bps slippage on constant-product only causes ~5 bps on a 20x concentrated position.
Tick Boundary Crossings
When a trade is large enough to exhaust liquidity in the current tick range, it crosses into the next tick where:
- Liquidity may be different (higher, lower, or zero)
- Each crossing adds a discrete jump in slippage
This makes CLMM slippage non-smooth — it can jump sharply at tick boundaries. Empirical measurement (via Jupiter quotes) captures this better than theoretical models.
Multi-Pool Routing
Optimal Split Across Pools
When Jupiter routes across n pools with reserves (x_1, y_1), ..., (x_n, y_n), the optimal split minimizes total slippage.
For constant-product pools, the optimal fraction to route through pool i:
fraction_i = sqrt(x_i * y_i) / sum_j(sqrt(x_j * y_j))
= sqrt(L_i) / sum_j(sqrt(L_j))Where L_i = x_i * y_i is the pool's liquidity (k-value).
Routing Through Intermediaries
Jupiter may route SOL → USDC → TOKEN if the SOL/TOKEN direct pool is thin. Total slippage compounds:
total_slippage ≈ slippage_hop1 + slippage_hop2(Approximate for small slippage values; exact: 1 - (1-s1)*(1-s2))
Why Empirical > Theoretical
Theoretical models assume:
- Known pool reserves (may be stale)
- Single pool type (ignoring CLMM/AMM mix)
- No intermediary routing
Jupiter's actual quotes incorporate all of this. Querying at multiple sizes gives the true executable slippage curve.
Power-Law Slippage Model
Empirically, slippage curves follow a power law:
slippage_bps = a * trade_size_sol ^ bWhere:
a= slippage coefficient (higher = less liquid)b= slippage exponent (typically 0.8–1.2; 1.0 = perfectly linear)
Fitting: Use log-linear regression on (log(size), log(slippage)) pairs from Jupiter quotes.
Inverting (find max size for a slippage limit):
max_size = (limit_bps / a) ^ (1/b)This model works well for interpolation within the fitted range. Extrapolation beyond the largest tested size is unreliable.
#!/usr/bin/env python3
"""Estimate total execution cost and break-even for a Solana DEX trade.
Computes all cost components — price impact, DEX fee, priority fee, and
MEV risk — then calculates the minimum price move needed to break even
on a roundtrip trade.
Usage:
python scripts/execution_cost.py
python scripts/execution_cost.py --demo
TOKEN_MINT=<mint> TRADE_SIZE_SOL=5.0 python scripts/execution_cost.py
Dependencies:
uv pip install httpx
Environment Variables:
TOKEN_MINT: Solana token mint address (optional, defaults to BONK).
TRADE_SIZE_SOL: Trade size in SOL (optional, defaults to 1.0).
"""
import argparse
import math
import os
import sys
from dataclasses import dataclass
from typing import Optional
try:
import httpx
except ImportError:
print("Missing dependency. Install with: uv pip install httpx")
sys.exit(1)
# ── Configuration ───────────────────────────────────────────────────
JUPITER_QUOTE_URL = "https://quote-api.jup.ag/v6/quote"
SOL_MINT = "So11111111111111111111111111111111111111112"
SOL_DECIMALS = 9
DEFAULT_TOKEN_MINT = "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263"
DEFAULT_TRADE_SIZE = 1.0
# Priority fee estimates by congestion level
PRIORITY_FEE_LOW = 0.0001 # SOL
PRIORITY_FEE_NORMAL = 0.001 # SOL
PRIORITY_FEE_HIGH = 0.005 # SOL
# MEV risk thresholds (trade size in SOL)
MEV_THRESHOLD_LOW = 1.0
MEV_THRESHOLD_MED = 5.0
MEV_THRESHOLD_HIGH = 20.0
REQUEST_TIMEOUT = 15.0
# ── Data Classes ────────────────────────────────────────────────────
@dataclass
class CostBreakdown:
"""Complete execution cost breakdown for a trade."""
trade_size_sol: float
direction: str # "buy" or "sell"
# Component costs in basis points
impact_bps: float
fee_bps: float
priority_bps: float
mev_bps: float
# Derived
total_bps: float
total_sol: float
# Quote details
output_amount: float
effective_price: float
spot_estimate: float # estimated spot from small quote
# Route info
route_description: str
@dataclass
class BreakEvenAnalysis:
"""Break-even analysis for a roundtrip trade."""
entry_cost_bps: float
exit_cost_bps: float
roundtrip_bps: float
roundtrip_pct: float
roundtrip_sol: float
required_price_move_pct: float
# ── Jupiter Quote ───────────────────────────────────────────────────
def fetch_quote(
input_mint: str,
output_mint: str,
amount_lamports: int,
) -> Optional[dict]:
"""Fetch a Jupiter V6 quote.
Args:
input_mint: Input token mint.
output_mint: Output token mint.
amount_lamports: Input amount in smallest units.
Returns:
Parsed JSON response or None on error.
"""
params = {
"inputMint": input_mint,
"outputMint": output_mint,
"amount": str(amount_lamports),
"slippageBps": "5000",
}
try:
with httpx.Client(timeout=REQUEST_TIMEOUT) as client:
resp = client.get(JUPITER_QUOTE_URL, params=params)
resp.raise_for_status()
return resp.json()
except (httpx.HTTPStatusError, httpx.RequestError) as e:
print(f"Quote error: {e}")
return None
def extract_fee_bps(quote_data: dict) -> float:
"""Extract total swap fee from Jupiter route plan.
Args:
quote_data: Raw Jupiter quote response.
Returns:
Estimated fee in basis points.
"""
route_plan = quote_data.get("routePlan", [])
if not route_plan:
return 25.0 # Default assumption: Raydium 0.25%
total_fee_pct = 0.0
for step in route_plan:
swap_info = step.get("swapInfo", {})
fee_amount = int(swap_info.get("feeAmount", "0"))
in_amount = int(swap_info.get("inAmount", "1"))
if in_amount > 0:
total_fee_pct += fee_amount / in_amount
return total_fee_pct * 10_000 if total_fee_pct > 0 else 25.0
def describe_route(quote_data: dict) -> str:
"""Generate human-readable route description.
Args:
quote_data: Raw Jupiter quote response.
Returns:
String describing the route.
"""
route_plan = quote_data.get("routePlan", [])
if not route_plan:
return "Unknown route"
labels = []
for step in route_plan:
swap_info = step.get("swapInfo", {})
label = swap_info.get("label", "Unknown")
pct = step.get("percent", 100)
labels.append(f"{label} ({pct}%)")
return " → ".join(labels)
# ── Cost Estimation ─────────────────────────────────────────────────
def estimate_priority_fee_bps(trade_size_sol: float, congestion: str = "normal") -> float:
"""Estimate priority fee in bps relative to trade size.
Args:
trade_size_sol: Trade size in SOL.
congestion: Network congestion level ('low', 'normal', 'high').
Returns:
Priority fee cost in basis points.
"""
fee_sol = {
"low": PRIORITY_FEE_LOW,
"normal": PRIORITY_FEE_NORMAL,
"high": PRIORITY_FEE_HIGH,
}.get(congestion, PRIORITY_FEE_NORMAL)
if trade_size_sol <= 0:
return 0.0
return (fee_sol / trade_size_sol) * 10_000
def estimate_mev_bps(trade_size_sol: float, pool_tvl_estimate: str = "medium") -> float:
"""Estimate MEV risk cost in basis points.
Uses trade size and estimated pool liquidity to assess sandwich
attack risk. Larger trades on thinner pools face higher MEV cost.
Args:
trade_size_sol: Trade size in SOL.
pool_tvl_estimate: Pool liquidity category ('deep', 'medium', 'thin').
Returns:
Estimated MEV cost in basis points.
"""
# Base MEV risk by pool depth
base_mev = {
"deep": 2.0, # >$1M TVL
"medium": 10.0, # $100K-$1M TVL
"thin": 30.0, # <$100K TVL
}.get(pool_tvl_estimate, 10.0)
# Scale with trade size (larger trades attract more MEV)
if trade_size_sol < MEV_THRESHOLD_LOW:
size_mult = 0.5
elif trade_size_sol < MEV_THRESHOLD_MED:
size_mult = 1.0
elif trade_size_sol < MEV_THRESHOLD_HIGH:
size_mult = 2.0
else:
size_mult = 4.0
return base_mev * size_mult
def estimate_execution_cost(
token_mint: str,
trade_size_sol: float,
direction: str = "buy",
congestion: str = "normal",
pool_depth: str = "medium",
) -> Optional[CostBreakdown]:
"""Estimate total execution cost for a trade.
Fetches Jupiter quotes at the target size and a small reference size,
computes price impact, adds fee/priority/MEV estimates.
Args:
token_mint: Token mint address.
trade_size_sol: Trade size in SOL.
direction: 'buy' (SOL -> token) or 'sell' (token -> SOL).
congestion: Network congestion level.
pool_depth: Estimated pool liquidity depth.
Returns:
CostBreakdown or None if quotes fail.
"""
# Fetch small reference quote (0.001 SOL) for spot price estimate
ref_lamports = int(0.001 * 10**SOL_DECIMALS)
ref_quote = fetch_quote(SOL_MINT, token_mint, ref_lamports)
if not ref_quote:
print("Failed to fetch reference quote.")
return None
ref_out = int(ref_quote.get("outAmount", 0))
if ref_out <= 0:
print("Reference quote returned zero output.")
return None
spot_estimate = 0.001 / ref_out # SOL per raw unit
# Fetch actual trade quote
trade_lamports = int(trade_size_sol * 10**SOL_DECIMALS)
trade_quote = fetch_quote(SOL_MINT, token_mint, trade_lamports)
if not trade_quote:
print("Failed to fetch trade quote.")
return None
trade_out = int(trade_quote.get("outAmount", 0))
if trade_out <= 0:
print("Trade quote returned zero output.")
return None
effective_price = trade_size_sol / trade_out # SOL per raw unit
# Price impact: how much worse than spot
impact_frac = (effective_price - spot_estimate) / spot_estimate if spot_estimate > 0 else 0
impact_bps = max(0.0, impact_frac * 10_000)
# Fee from route
fee_bps = extract_fee_bps(trade_quote)
# Priority fee
priority_bps = estimate_priority_fee_bps(trade_size_sol, congestion)
# MEV risk
mev_bps = estimate_mev_bps(trade_size_sol, pool_depth)
total_bps = impact_bps + fee_bps + priority_bps + mev_bps
total_sol = trade_size_sol * total_bps / 10_000
return CostBreakdown(
trade_size_sol=trade_size_sol,
direction=direction,
impact_bps=round(impact_bps, 2),
fee_bps=round(fee_bps, 2),
priority_bps=round(priority_bps, 2),
mev_bps=round(mev_bps, 2),
total_bps=round(total_bps, 2),
total_sol=round(total_sol, 6),
output_amount=trade_out,
effective_price=effective_price,
spot_estimate=spot_estimate,
route_description=describe_route(trade_quote),
)
def estimate_execution_cost_demo(
trade_size_sol: float,
direction: str = "buy",
) -> CostBreakdown:
"""Generate demo execution cost estimate with synthetic data.
Simulates a mid-cap token with ~$300K TVL.
Args:
trade_size_sol: Trade size in SOL.
direction: Trade direction.
Returns:
Synthetic CostBreakdown.
"""
# Simulated pool: 750 SOL reserves
pool_sol = 750.0
impact_frac = trade_size_sol / (pool_sol + trade_size_sol)
impact_bps = round(impact_frac * 10_000, 2)
fee_bps = 25.0 # Raydium standard
priority_bps = round(estimate_priority_fee_bps(trade_size_sol, "normal"), 2)
mev_bps = round(estimate_mev_bps(trade_size_sol, "medium"), 2)
total_bps = impact_bps + fee_bps + priority_bps + mev_bps
total_sol = trade_size_sol * total_bps / 10_000
spot_price = 0.0001
eff_price = spot_price * (1 + impact_frac)
return CostBreakdown(
trade_size_sol=trade_size_sol,
direction=direction,
impact_bps=impact_bps,
fee_bps=fee_bps,
priority_bps=priority_bps,
mev_bps=mev_bps,
total_bps=round(total_bps, 2),
total_sol=round(total_sol, 6),
output_amount=int(trade_size_sol / eff_price),
effective_price=eff_price,
spot_estimate=spot_price,
route_description="Raydium AMM (100%) [demo]",
)
# ── Break-Even Analysis ────────────────────────────────────────────
def compute_break_even(
entry_cost: CostBreakdown,
exit_multiplier: float = 1.2,
) -> BreakEvenAnalysis:
"""Compute break-even price move for a roundtrip trade.
Exit slippage is typically higher than entry (selling into a pool
you just bought from). exit_multiplier scales the entry cost.
Args:
entry_cost: Cost breakdown for the entry trade.
exit_multiplier: Factor to scale entry costs for exit estimate.
Returns:
BreakEvenAnalysis with roundtrip costs.
"""
exit_impact = entry_cost.impact_bps * exit_multiplier
exit_bps = exit_impact + entry_cost.fee_bps + entry_cost.priority_bps + entry_cost.mev_bps * 0.5
roundtrip_bps = entry_cost.total_bps + exit_bps
roundtrip_pct = roundtrip_bps / 100.0
roundtrip_sol = entry_cost.trade_size_sol * roundtrip_bps / 10_000
required_move = roundtrip_bps / (10_000 - roundtrip_bps) * 100
return BreakEvenAnalysis(
entry_cost_bps=round(entry_cost.total_bps, 2),
exit_cost_bps=round(exit_bps, 2),
roundtrip_bps=round(roundtrip_bps, 2),
roundtrip_pct=round(roundtrip_pct, 4),
roundtrip_sol=round(roundtrip_sol, 6),
required_price_move_pct=round(required_move, 4),
)
# ── Display ─────────────────────────────────────────────────────────
def print_cost_report(cost: CostBreakdown, break_even: BreakEvenAnalysis) -> None:
"""Print formatted execution cost report.
Args:
cost: Execution cost breakdown.
break_even: Break-even analysis.
"""
print("\n" + "=" * 60)
print("EXECUTION COST REPORT")
print("=" * 60)
print(f"Direction: {cost.direction.upper()}")
print(f"Trade size: {cost.trade_size_sol:.4f} SOL")
print(f"Route: {cost.route_description}")
print("\n--- Cost Components ---")
print(f" Price impact: {cost.impact_bps:>8.2f} bps")
print(f" DEX fee: {cost.fee_bps:>8.2f} bps")
print(f" Priority fee: {cost.priority_bps:>8.2f} bps")
print(f" MEV risk: {cost.mev_bps:>8.2f} bps")
print(f" ────────────────────────────")
print(f" TOTAL: {cost.total_bps:>8.2f} bps ({cost.total_bps / 100:.2f}%)")
print(f" Cost in SOL: {cost.total_sol:>8.6f} SOL")
print("\n--- Break-Even Analysis ---")
print(f" Entry cost: {break_even.entry_cost_bps:>8.2f} bps")
print(f" Exit cost (est): {break_even.exit_cost_bps:>8.2f} bps")
print(f" Roundtrip cost: {break_even.roundtrip_bps:>8.2f} bps ({break_even.roundtrip_pct:.2f}%)")
print(f" Roundtrip in SOL: {break_even.roundtrip_sol:>8.6f} SOL")
print(f" Min price move: {break_even.required_price_move_pct:>8.4f}%")
print("\n--- Assessment ---")
if break_even.roundtrip_bps < 100:
print(" LOW COST — Favorable execution. Most strategies can absorb this.")
elif break_even.roundtrip_bps < 300:
print(" MODERATE COST — Ensure expected move exceeds break-even threshold.")
elif break_even.roundtrip_bps < 1000:
print(" HIGH COST — Only viable for high-conviction, large-move trades.")
else:
print(" VERY HIGH COST — Consider smaller size or higher-liquidity alternatives.")
print("\n" + "=" * 60)
print("NOTE: This is an estimate for informational purposes.")
print("Actual costs may differ due to market conditions.")
print("=" * 60)
# ── Main ────────────────────────────────────────────────────────────
def main() -> None:
"""Run execution cost estimation."""
parser = argparse.ArgumentParser(description="Estimate DEX execution costs")
parser.add_argument("--demo", action="store_true", help="Use synthetic data")
parser.add_argument("--token", type=str, default=None, help="Token mint address")
parser.add_argument("--size", type=float, default=None, help="Trade size in SOL")
parser.add_argument("--direction", type=str, default="buy", choices=["buy", "sell"])
parser.add_argument("--congestion", type=str, default="normal", choices=["low", "normal", "high"])
parser.add_argument("--pool-depth", type=str, default="medium", choices=["deep", "medium", "thin"])
args = parser.parse_args()
token_mint = args.token or os.getenv("TOKEN_MINT", DEFAULT_TOKEN_MINT)
trade_size = args.size or float(os.getenv("TRADE_SIZE_SOL", str(DEFAULT_TRADE_SIZE)))
if args.demo:
print("DEMO MODE — using synthetic cost estimates")
print(f"Simulated mid-cap token, pool TVL ~$300K")
cost = estimate_execution_cost_demo(trade_size, args.direction)
else:
print(f"Estimating costs for {trade_size:.4f} SOL {args.direction}")
print(f"Token: {token_mint}")
cost = estimate_execution_cost(
token_mint, trade_size, args.direction, args.congestion, args.pool_depth
)
if cost is None:
print("Failed to estimate execution cost.")
sys.exit(1)
break_even = compute_break_even(cost)
print_cost_report(cost, break_even)
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""Build empirical slippage curves from Jupiter quotes or synthetic data.
Queries Jupiter V6 API at multiple trade sizes to measure actual executable
slippage, fits a power-law model, and estimates maximum trade sizes for
various slippage thresholds.
Usage:
python scripts/slippage_curve.py
python scripts/slippage_curve.py --demo
TOKEN_MINT=<mint_address> python scripts/slippage_curve.py
Dependencies:
uv pip install httpx
Environment Variables:
TOKEN_MINT: Solana token mint address to analyze (optional, defaults to
BONK for demonstration).
"""
import argparse
import json
import math
import os
import sys
import time
from dataclasses import dataclass, field
from typing import Optional
try:
import httpx
except ImportError:
print("Missing dependency. Install with: uv pip install httpx")
sys.exit(1)
# ── Configuration ───────────────────────────────────────────────────
JUPITER_QUOTE_URL = "https://quote-api.jup.ag/v6/quote"
SOL_MINT = "So11111111111111111111111111111111111111112"
SOL_DECIMALS = 9
# Default token: BONK (widely available, decent liquidity)
DEFAULT_TOKEN_MINT = "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263"
# Trade sizes to test (in SOL)
TRADE_SIZES_SOL = [0.01, 0.05, 0.1, 0.25, 0.5, 1.0, 2.0, 5.0, 10.0, 25.0, 50.0]
# Request timeout and delay
REQUEST_TIMEOUT = 15.0
REQUEST_DELAY = 0.5 # seconds between requests to avoid rate limiting
# ── Data Classes ────────────────────────────────────────────────────
@dataclass
class QuoteResult:
"""Result from a single Jupiter quote query."""
trade_size_sol: float
input_lamports: int
output_amount: int
output_decimals: int
effective_price: float # SOL per token
route_plan: list[dict] = field(default_factory=list)
error: Optional[str] = None
@dataclass
class SlippagePoint:
"""A single point on the slippage curve."""
trade_size_sol: float
slippage_bps: float
effective_price: float
output_tokens: float
@dataclass
class PowerLawFit:
"""Parameters of the power-law slippage model: bps = a * size^b."""
a: float
b: float
r_squared: float
# ── Jupiter Quote Functions ─────────────────────────────────────────
def fetch_jupiter_quote(
input_mint: str,
output_mint: str,
amount_lamports: int,
slippage_bps: int = 5000,
timeout: float = REQUEST_TIMEOUT,
) -> QuoteResult:
"""Fetch a single quote from Jupiter V6 API.
Args:
input_mint: Input token mint address.
output_mint: Output token mint address.
amount_lamports: Input amount in smallest unit (lamports for SOL).
slippage_bps: Maximum slippage tolerance in basis points.
timeout: HTTP request timeout in seconds.
Returns:
QuoteResult with execution details.
"""
params = {
"inputMint": input_mint,
"outputMint": output_mint,
"amount": str(amount_lamports),
"slippageBps": str(slippage_bps),
}
try:
with httpx.Client(timeout=timeout) as client:
resp = client.get(JUPITER_QUOTE_URL, params=params)
resp.raise_for_status()
data = resp.json()
except httpx.HTTPStatusError as e:
return QuoteResult(
trade_size_sol=amount_lamports / 10**SOL_DECIMALS,
input_lamports=amount_lamports,
output_amount=0,
output_decimals=0,
effective_price=0.0,
error=f"HTTP {e.response.status_code}: {e.response.text[:200]}",
)
except httpx.RequestError as e:
return QuoteResult(
trade_size_sol=amount_lamports / 10**SOL_DECIMALS,
input_lamports=amount_lamports,
output_amount=0,
output_decimals=0,
effective_price=0.0,
error=f"Request error: {str(e)[:200]}",
)
out_amount = int(data.get("outAmount", 0))
# Infer decimals from the first route or default
out_decimals = _infer_output_decimals(data)
trade_sol = amount_lamports / 10**SOL_DECIMALS
tokens = out_amount / 10**out_decimals if out_decimals > 0 else out_amount
eff_price = trade_sol / tokens if tokens > 0 else 0.0
route_plan = data.get("routePlan", [])
return QuoteResult(
trade_size_sol=trade_sol,
input_lamports=amount_lamports,
output_amount=out_amount,
output_decimals=out_decimals,
effective_price=eff_price,
route_plan=route_plan,
)
def _infer_output_decimals(quote_data: dict) -> int:
"""Infer output token decimals from quote response data.
Args:
quote_data: Raw Jupiter quote response.
Returns:
Number of decimal places for the output token.
"""
# Jupiter V6 doesn't always return decimals directly.
# Use a heuristic: if outAmount is very large relative to a
# reasonable token amount, it has many decimals.
out_amount = int(quote_data.get("outAmount", 0))
in_amount = int(quote_data.get("inAmount", 1))
# For well-known tokens we could hardcode, but generically:
# Most SPL tokens use 6 or 9 decimals.
# Check if routePlan has decimal info.
route_plan = quote_data.get("routePlan", [])
for step in route_plan:
swap_info = step.get("swapInfo", {})
out_mint = swap_info.get("outputMint", "")
# If the last hop outputs SOL, it's 9 decimals
if out_mint == SOL_MINT:
return SOL_DECIMALS
# Default assumption for most SPL tokens
if out_amount > 10**15:
return 9
if out_amount > 10**10:
return 6
return 6
# ── Slippage Curve Building ─────────────────────────────────────────
def build_slippage_curve_live(
token_mint: str,
trade_sizes: list[float],
direction: str = "buy",
) -> list[SlippagePoint]:
"""Build slippage curve by querying Jupiter at multiple trade sizes.
Args:
token_mint: Token mint address to analyze.
trade_sizes: List of trade sizes in SOL.
direction: 'buy' (SOL -> token) or 'sell' (token -> SOL).
Returns:
List of SlippagePoint objects sorted by trade size.
"""
quotes: list[QuoteResult] = []
for size_sol in sorted(trade_sizes):
lamports = int(size_sol * 10**SOL_DECIMALS)
if direction == "buy":
quote = fetch_jupiter_quote(SOL_MINT, token_mint, lamports)
else:
# For sell, we'd need token amount, not SOL amount.
# Approximate by using the buy direction for curve shape.
quote = fetch_jupiter_quote(SOL_MINT, token_mint, lamports)
if quote.error:
print(f" WARNING: {size_sol} SOL — {quote.error}")
else:
quotes.append(quote)
print(f" {size_sol:>8.2f} SOL → {quote.output_amount} raw units")
time.sleep(REQUEST_DELAY)
if len(quotes) < 2:
print("ERROR: Need at least 2 successful quotes to build curve.")
return []
# Use smallest trade as reference price (closest to spot)
ref_price = quotes[0].effective_price
if ref_price <= 0:
print("ERROR: Reference price is zero. Cannot compute slippage.")
return []
points: list[SlippagePoint] = []
for q in quotes:
if q.effective_price <= 0:
continue
# Slippage = how much more you pay vs reference
slippage_frac = (q.effective_price - ref_price) / ref_price
slippage_bps = slippage_frac * 10_000
tokens = q.output_amount / 10**q.output_decimals if q.output_decimals > 0 else float(q.output_amount)
points.append(SlippagePoint(
trade_size_sol=q.trade_size_sol,
slippage_bps=max(0.0, slippage_bps),
effective_price=q.effective_price,
output_tokens=tokens,
))
return points
def build_slippage_curve_demo(trade_sizes: list[float]) -> list[SlippagePoint]:
"""Generate synthetic slippage curve for demonstration.
Simulates a mid-cap token with ~$200K TVL pool.
Model: slippage_bps = 15 * trade_size^1.05
Args:
trade_sizes: List of trade sizes in SOL.
Returns:
List of SlippagePoint objects.
"""
# Simulated pool: 500 SOL / 5,000,000 tokens
pool_sol = 500.0
pool_tokens = 5_000_000.0
spot_price = pool_sol / pool_tokens # 0.0001 SOL/token
points: list[SlippagePoint] = []
for size in sorted(trade_sizes):
# Constant-product with some noise for realism
tokens_out = pool_tokens * size / (pool_sol + size)
eff_price = size / tokens_out if tokens_out > 0 else 0
slippage_frac = (eff_price - spot_price) / spot_price
slippage_bps = slippage_frac * 10_000
# Add small random-ish perturbation (deterministic based on size)
noise = math.sin(size * 7.3) * 0.5 + 1.0 # 0.5x to 1.5x
slippage_bps = max(0.01, slippage_bps * (0.9 + 0.2 * (noise / 1.5)))
points.append(SlippagePoint(
trade_size_sol=size,
slippage_bps=round(slippage_bps, 2),
effective_price=eff_price,
output_tokens=tokens_out,
))
return points
# ── Power-Law Fitting ───────────────────────────────────────────────
def fit_power_law(points: list[SlippagePoint]) -> Optional[PowerLawFit]:
"""Fit slippage_bps = a * trade_size^b using log-linear regression.
Args:
points: Slippage data points (must have at least 2 with slippage > 0).
Returns:
PowerLawFit with coefficients and R-squared, or None if fitting fails.
"""
# Filter to points with positive slippage
valid = [(p.trade_size_sol, p.slippage_bps) for p in points if p.slippage_bps > 0 and p.trade_size_sol > 0]
if len(valid) < 2:
return None
# Log-transform
log_x = [math.log(s) for s, _ in valid]
log_y = [math.log(b) for _, b in valid]
n = len(valid)
sum_x = sum(log_x)
sum_y = sum(log_y)
sum_xy = sum(lx * ly for lx, ly in zip(log_x, log_y))
sum_x2 = sum(lx * lx for lx in log_x)
denom = n * sum_x2 - sum_x * sum_x
if abs(denom) < 1e-12:
return None
b = (n * sum_xy - sum_x * sum_y) / denom
log_a = (sum_y - b * sum_x) / n
a = math.exp(log_a)
# R-squared
mean_y = sum_y / n
ss_tot = sum((ly - mean_y) ** 2 for ly in log_y)
ss_res = sum((ly - (log_a + b * lx)) ** 2 for lx, ly in zip(log_x, log_y))
r_sq = 1.0 - (ss_res / ss_tot) if ss_tot > 0 else 0.0
return PowerLawFit(a=a, b=b, r_squared=r_sq)
def max_trade_size(fit: PowerLawFit, threshold_bps: float) -> float:
"""Compute maximum trade size for a given slippage threshold.
Args:
fit: Power-law fit parameters.
threshold_bps: Maximum acceptable slippage in basis points.
Returns:
Maximum trade size in SOL.
"""
if fit.a <= 0 or fit.b == 0:
return 0.0
return (threshold_bps / fit.a) ** (1.0 / fit.b)
# ── Display Functions ───────────────────────────────────────────────
def print_slippage_table(points: list[SlippagePoint]) -> None:
"""Print formatted slippage curve table.
Args:
points: List of slippage data points.
"""
print("\n" + "=" * 72)
print("SLIPPAGE CURVE")
print("=" * 72)
print(f"{'Trade Size (SOL)':>18} {'Slippage (bps)':>16} {'Slippage (%)':>14} {'Eff. Price':>14}")
print("-" * 72)
for p in points:
pct = p.slippage_bps / 100.0
print(f"{p.trade_size_sol:>18.4f} {p.slippage_bps:>16.2f} {pct:>13.4f}% {p.effective_price:>14.10f}")
print("-" * 72)
def print_fit_results(fit: PowerLawFit) -> None:
"""Print power-law fit results and max trade size estimates.
Args:
fit: Fitted power-law model parameters.
"""
print(f"\nPOWER-LAW FIT: slippage_bps = {fit.a:.4f} * trade_size ^ {fit.b:.4f}")
print(f"R-squared: {fit.r_squared:.4f}")
thresholds = [25, 50, 100, 200, 500, 1000]
print(f"\n{'Threshold (bps)':>18} {'Max Trade (SOL)':>18}")
print("-" * 40)
for t in thresholds:
max_sol = max_trade_size(fit, t)
if max_sol > 10_000:
print(f"{t:>18} {'> 10,000':>18}")
else:
print(f"{t:>18} {max_sol:>18.4f}")
print("-" * 40)
def print_tranche_recommendation(fit: PowerLawFit, total_size: float, threshold_bps: float = 100.0) -> None:
"""Print multi-tranche execution recommendation.
Args:
fit: Power-law fit parameters.
total_size: Total desired trade size in SOL.
threshold_bps: Maximum slippage per tranche in bps.
"""
max_sol = max_trade_size(fit, threshold_bps)
if max_sol <= 0:
print("\nCannot determine tranche size — fit parameters invalid.")
return
if total_size <= max_sol:
single_slip = fit.a * total_size**fit.b
print(f"\nTRANCHE ANALYSIS for {total_size:.2f} SOL:")
print(f" Single execution: {single_slip:.1f} bps slippage")
print(f" Max size for {threshold_bps:.0f} bps: {max_sol:.4f} SOL")
print(f" Recommendation: Execute as single trade")
return
n_tranches = math.ceil(total_size / max_sol)
tranche_size = total_size / n_tranches
per_tranche_slip = fit.a * tranche_size**fit.b
single_slip = fit.a * total_size**fit.b
print(f"\nTRANCHE ANALYSIS for {total_size:.2f} SOL:")
print(f" Single execution: {single_slip:.1f} bps slippage")
print(f" Max size for {threshold_bps:.0f} bps: {max_sol:.4f} SOL")
print(f" Recommended tranches: {n_tranches}")
print(f" Tranche size: {tranche_size:.4f} SOL")
print(f" Per-tranche slippage: {per_tranche_slip:.1f} bps")
print(f" Wait between tranches: 5-10 seconds (allow arb rebalancing)")
# ── Main ────────────────────────────────────────────────────────────
def main() -> None:
"""Run slippage curve analysis."""
parser = argparse.ArgumentParser(description="Build empirical slippage curves")
parser.add_argument("--demo", action="store_true", help="Use synthetic data instead of live API")
parser.add_argument("--token", type=str, default=None, help="Token mint address")
parser.add_argument("--total-size", type=float, default=10.0, help="Total trade size for tranche analysis (SOL)")
parser.add_argument("--threshold", type=float, default=100.0, help="Slippage threshold for tranche analysis (bps)")
args = parser.parse_args()
token_mint = args.token or os.getenv("TOKEN_MINT", DEFAULT_TOKEN_MINT)
if args.demo:
print("DEMO MODE — using synthetic slippage curve")
print("Simulated pool: 500 SOL / 5,000,000 tokens (~$200K TVL)")
points = build_slippage_curve_demo(TRADE_SIZES_SOL)
else:
print(f"Querying Jupiter for token: {token_mint}")
print(f"Testing {len(TRADE_SIZES_SOL)} trade sizes...")
points = build_slippage_curve_live(token_mint, TRADE_SIZES_SOL, direction="buy")
if not points:
print("No data points collected. Exiting.")
sys.exit(1)
print_slippage_table(points)
fit = fit_power_law(points)
if fit:
print_fit_results(fit)
print_tranche_recommendation(fit, args.total_size, args.threshold)
else:
print("\nCould not fit power-law model (insufficient valid data points).")
print("\nNOTE: This analysis is for informational purposes only.")
print("Actual execution slippage may differ from quotes due to")
print("price movement, MEV, and changing liquidity conditions.")
if __name__ == "__main__":
main()
Related skills
FAQ
How does slippage scale with trade size on a constant-product AMM?
Price impact equals trade size divided by reserves plus trade size, so it is roughly linear for small trades and accelerates sharply as the trade approaches pool reserves.
What goes into total execution cost?
Price impact in basis points plus DEX fees, priority fees, and MEV risk, summed and applied to trade size to give total cost in SOL.