← Zurück zu den Artikeln
July 18, 2026
5 min read

Uniswap v3 für Quants: Concentrated Liquidity und Tick-Mathematik von Grund auf

Uniswap v3 für Quants: Concentrated Liquidity und Tick-Mathematik von Grund auf
#uniswap
#defi
#amm
#concentrated liquidity
#market making
#lvr
#quant
#ethereum

Eine LP-Position auf Uniswap v3 ist eine getarnte Range-Order aus dem Limit-Orderbuch. Hinterlegt man Liquidität zwischen zwei Preisen, verpflichtet man sich, den Asset auf dem Weg nach unten innerhalb dieser Range zu kaufen und auf dem Weg nach oben zu verkaufen — genau das, was ein Gitter ruhender Limit-Orders auf einem CLOB tut. Der Smart Contract speichert jedoch keine Preise, Mengen oder ein Orderbuch. Er speichert drei Zahlen: einen Quadratwurzel-Preis im Q64.96-Festkommaformat, einen ganzzahligen Tick-Index und einen aggregierten Liquiditätswert LL. Wer eine v3-Position so durchdenken will wie Quotes in einem Market-Making-Modell — Inventar, Fill-Preise, Adverse Selection — muss zwischen diesen On-Chain-Primitiven und den Trading-Konzepten übersetzen können, die sie kodieren. Dieser Artikel baut diese Übersetzung von Grund auf, entlang des Uniswap-v3-Whitepapers (Adams, Zinsmeister, Salem, Keefer, Robinson, 2021) und der Kern-Contracts, und endet dort, wo jede ernsthafte LP-Diskussion heute beginnt: bei Loss-versus-Rebalancing.

Von x·y = k zu virtuellen Reserven

Uniswap v2 ist der Constant-Product-Market-Maker: ein Pool hält Reserven xx von Token0 und yy von Token1 und erzwingt bei jedem Swap

xy=kx \cdot y = k

Der Marginalpreis von Token0 in Einheiten von Token1 ist P=y/xP = y/x, und die Liquidität ist gleichmäßig über die gesamte Preisachse (0,)(0, \infty) verteilt. Das ist extrem kapitalineffizient: Ein Stablecoin-Paar, das zwischen 0,999 und 1,001 handelt, hält mehr als 99% seines Kapitals bei Preisen, die niemals gedruckt werden.

Der Clou von v3 besteht darin, jedem LP zu erlauben, sein Kapital auf eine Range [pa,pb][p_a, p_b] zu beschränken. Innerhalb der Range muss sich die Position exakt wie ein v2-Pool verhalten — gleiche Bonding-Curve, gleiche Marginalpreisbildung — jedoch nur mit den Reserven, die zur Abdeckung dieser Range nötig sind. Das Whitepaper formalisiert dies mit virtuellen Reserven: Die Position verhält sich, als hielte sie v2-artige Reserven (xv,yv)(x_v, y_v) auf einer Kurve xvyv=L2x_v \cdot y_v = L^2, während die realen Reserven die virtuellen abzüglich dessen sind, was die Position an den Range-Grenzen halten würde:

(x+Lpb)(y+Lpa)=L2\left(x + \frac{L}{\sqrt{p_b}}\right)\left(y + L\sqrt{p_a}\right) = L^2

Dies ist Gleichung 2.2 des Whitepapers und die mit Abstand wichtigste Gleichung in v3. Die transformierte Kurve berührt die Achsen: bei P=pbP = p_b fällt die reale xx-Reserve auf null (Position ist zu 100% Token1), und bei P=paP = p_a fällt die reale yy-Reserve auf null (100% Token0). Außerhalb der Range wird die Position inaktiv — ein fester Bestand eines einzigen Tokens, der nichts mehr verdient.

Uniswap-v2-Constant-Product-Kurve im Vergleich zur transformierten v3-Kurve mit virtuellen und realen Reserven

Der Parameter LL, genannt Liquidität, ist die Invariante, die kk ersetzt. Er ist definiert als L=kL = \sqrt{k} und hat eine klare Interpretation, die das Whitepaper explizit macht: Liquidität ist "virtuelle Reserven pro Einheit Quadratwurzel-Preis". Bei jedem Preis PP innerhalb der Range gilt für die virtuellen Reserven

xv=LP,yv=LPx_v = \frac{L}{\sqrt{P}}, \qquad y_v = L\sqrt{P}

Warum die Zustandsvariable √P ist

Der Kern von v3 verfolgt nicht den Preis. Er verfolgt P\sqrt{P}, gespeichert als sqrtPriceX96, eine vorzeichenlose Q64.96-Festkommazahl:

sqrtPriceX96=P296\texttt{sqrtPriceX96} = \sqrt{P} \cdot 2^{96}

wobei PP der rohe Preis ist: Token1-Basiseinheiten pro Token0-Basiseinheit, Dezimalstellen inklusive (dazu gleich mehr). Der Grund für die Quadratwurzel ist keine Gas-Optimierung, sondern Algebra. Differenziert man die virtuellen-Reserven-Identitäten, erhält man die beiden fundamentalen Swap-Gleichungen, implementiert in der SqrtPriceMath-Bibliothek:

Δy=LΔP,Δx=LΔ ⁣(1P)\Delta y = L \cdot \Delta\sqrt{P}, \qquad \Delta x = L \cdot \Delta\!\left(\frac{1}{\sqrt{P}}\right)

Beide Token-Deltas sind linear im Quadratwurzel-Preis (bzw. seinem Kehrwert), mit LL als Proportionalitätskonstante. Ein Swap innerhalb eines einzelnen Ticks benötigt daher keine Kurveninversion, keine Newton-Iteration — nur eine Multiplikation, um P\sqrt{P} zu verschieben, dann zwei Multiplikationen zur Berechnung der Beträge. Ist ein Swap groß genug, um den Preis über einen initialisierten Tick zu schieben, überquert der Pool ihn, addiert oder entfernt die dort referenzierte Netto-Liquidität (liquidityNet) und setzt mit dem neuen aggregierten LL fort. Global betrachtet ist der Pool ein stückweise konstanter Constant-Product-AMM: konstantes LL zwischen initialisierten Ticks, Sprünge an den Ticks.

Für einen Market Maker ist das das richtige mentale Modell: das aggregierte L(P)L(P)-Profil des Pools ist das DEX-Äquivalent zur Orderbuch-Tiefe. Wo CLOB-Tiefe in Einheiten pro Preisstufe notiert wird, ist AMM-Tiefe LL pro Tick — und die Umrechnung ist genau die oben genannte Identität Δy=LΔP\Delta y = L\Delta\sqrt{P}.

Ticks: ein logarithmisch gestaffeltes Preisraster

Ranges können nicht bei beliebigen Preisen beginnen und enden; sie müssen an Ticks einrasten. Tick ii entspricht dem Preis

p(i)=1.0001ip(i)=1.0001i/2p(i) = 1.0001^i \quad\Longrightarrow\quad \sqrt{p(i)} = 1.0001^{i/2}

sodass jeder Tick einen Basispunkt von seinen Nachbarn entfernt liegt — nicht in absoluten, sondern in relativen Größen. Diese logarithmische Staffelung ist bewusst gewählt: ein festes additives Raster wäre bei einem Token, das zu $0,0001 handelt, absurd grob und bei $100.000 absurd fein, während ein geometrisches Raster auf jeder Preisskala eine gleichmäßige 1-bp-Auflösung liefert. Die TickMath-Bibliothek rechnet in beide Richtungen um — getSqrtRatioAtTick und getTickAtSqrtRatio — mittels Bit-Manipulation über vorberechnete Konstanten statt eines Aufrufs einer Exponentialfunktion. Tick-Indizes sind begrenzt durch MIN_TICK = -887272 und MAX_TICK = 887272, was den Preisbereich [2128,2128][2^{-128}, 2^{128}] abdeckt: weit genug für jedes Token-Paar, das in uint256-Arithmetik existieren kann.

Nicht jeder Tick ist nutzbar. Jede Fee-Stufe erzwingt einen Tick-Spacing, und Positionen dürfen nur Ticks verwenden, die dadurch teilbar sind:

Fee-Stufe Tick-Spacing Min. Range-Breite Typische Nutzung
0,01% 1 ~1 bp Stable/Stable
0,05% 10 ~10 bp Stable-Paare, ETH/Stables
0,30% 60 ~60 bp Major-Paare
1,00% 200 ~2% Exotisch/volatil

Die Stufen 0,05%/0,30%/1% gingen im Mai 2021 live; die 0,01%-Stufe wurde im November 2021 per Governance-Abstimmung hinzugefügt. Ein gröberes Spacing bei Pools mit höherer Fee hält die Zahl potenziell überquerbarer Ticks gering und begrenzt die Gaskosten von Swaps.

Eine Dezimalstellen-Falle, in die jeder einmal tappt: Der rohe On-Chain-Preis ist Token1 pro Token0 in Basiseinheiten. Im Mainnet-USDC/WETH-Pool ist Token0 USDC (6 Dezimalstellen) und Token1 WETH (18 Dezimalstellen), sodass ein menschlicher Preis von $3.000 pro ETH einem rohen Preis von 1012/30003,333×10810^{12}/3000 \approx 3{,}333 \times 10^{8} WETH-Wei pro USDC-Einheit entspricht, was bei Tick

i=log1.0001 ⁣(3.333×108)=196,256i = \left\lfloor \log_{1.0001}\!\left(3.333\times10^{8}\right) \right\rfloor = 196{,}256

liegt, mit sqrtPriceX961,4465×1033\texttt{sqrtPriceX96} \approx 1{,}4465 \times 10^{33}. Wenn Ihr Monitoring-Dashboard für USDC/WETH einen Tick nahe 196k anzeigt, wissen Sie jetzt, warum — und dass der menschliche Preis (sqrtPriceX96/296)210dec0dec1\left(\texttt{sqrtPriceX96}/2^{96}\right)^2 \cdot 10^{\,\text{dec}_0 - \text{dec}_1} ist, bei Bedarf invertiert.

Token-Beträge aus (L, Preisrange): die exakten Formeln

Das Kernergebnis dieses Abschnitts: Gegeben eine Position mit Liquidität LL auf [pa,pb][p_a, p_b] und aktuellem Preis PP — was hält sie? Integriert man die Swap-Gleichungen über die Range, ergeben sich drei Fälle:

Preis unterhalb der Range (PpaP \le p_a) — die Position ist zu 100% Token0:

x=L(1pa1pb),y=0x = L\left(\frac{1}{\sqrt{p_a}} - \frac{1}{\sqrt{p_b}}\right), \qquad y = 0

Preis oberhalb der Range (PpbP \ge p_b) — die Position ist zu 100% Token1:

x=0,y=L(pbpa)x = 0, \qquad y = L\left(\sqrt{p_b} - \sqrt{p_a}\right)

Preis in der Range (pa<P<pbp_a < P < p_b):

x=L(1P1pb),y=L(Ppa)x = L\left(\frac{1}{\sqrt{P}} - \frac{1}{\sqrt{p_b}}\right), \qquad y = L\left(\sqrt{P} - \sqrt{p_a}\right)

Das ist genau das, was SqrtPriceMath.getAmount0Delta und getAmount1Delta berechnen (mit gerichtetem Runden — der Contract rundet immer zulasten des Nutzers, ein Detail, das relevant wird, wenn man dies in einem Backtester nachbildet und sich über Abweichungen auf Wei-Ebene wundert).

from math import sqrt

def position_amounts(L: float, pa: float, pb: float, P: float):
    """Token amounts held by a v3 position (float model; core uses Q96 ints)."""
    sa, sb, sp = sqrt(pa), sqrt(pb), sqrt(P)
    if P <= pa:
        return L * (1/sa - 1/sb), 0.0
    if P >= pb:
        return 0.0, L * (sb - sa)
    return L * (1/sp - 1/sb), L * (sp - sa)

Durchgerechnetes Beispiel: ETH/USDC, Range [2500, 3500]

Angenommen, ETH handelt bei P=3000P = 3000 USDC, und Sie möchten 1 ETH in die Range [2500,3500][2500, 3500] einzahlen. Quadratwurzeln: 2500=50\sqrt{2500} = 50, 3000=54,7723\sqrt{3000} = 54{,}7723, 3500=59,1608\sqrt{3500} = 59{,}1608.

Das ETH-Bein legt LL fest:

L=xPpbpbP=1×54.7723×59.160859.160854.7723=738.37L = \frac{x \cdot \sqrt{P}\,\sqrt{p_b}}{\sqrt{p_b} - \sqrt{P}} = \frac{1 \times 54.7723 \times 59.1608}{59.1608 - 54.7723} = 738.37

Das USDC-Bein folgt dann:

y=L(Ppa)=738.37×(54.772350)=3,523.69 USDCy = L\left(\sqrt{P} - \sqrt{p_a}\right) = 738.37 \times (54.7723 - 50) = 3{,}523.69 \text{ USDC}

Das Minten dieser Position erfordert also 1 ETH + 3.523,69 USDC, Gesamtwert $6.523,69, und beachten Sie: die beiden Beine sind nicht 50/50 gesplittet — der Split hängt davon ab, wo PP innerhalb der Range liegt (eine asymmetrische Range ist genau die Art, eine gerichtete Meinung auszudrücken, oder eine reine "Range-Order" mit nur einem Token zu platzieren).

Nun bewegen wir den Preis zu den Grenzen:

  • Bei P=2500P = 2500 hat sich die Position vollständig in ETH umgewandelt: x=738.37×(1/501/59.1608)=2,2867x = 738.37 \times (1/50 - 1/59.1608) = 2{,}2867 ETH, im Wert von $5.716,68. Sie haben 1,2867 ETH auf dem Weg nach unten zu einem liquiditätsgewichteten Durchschnittspreis von 3523.69 / 1.2867 \approx \2{,}739$ gekauft.
  • Bei P=3500P = 3500 hat sie sich vollständig in USDC umgewandelt: y=738.37×(59.160850)=6,764.06y = 738.37 \times (59.1608 - 50) = 6{,}764.06 USDC. Sie haben Ihr ETH auf dem Weg nach oben zu einem Durchschnitt von $3.240 verkauft.

Das ist die Range-Order-Lesart ganz konkret: Die Position ist eine Leiter aus Geboten (Bids) von 3000 abwärts bis 2500 und Angeboten (Asks) von 3000 aufwärts bis 3500, mit einer Größe pro Tick proportional zu LL. Und Konzentration ist das, wofür man bezahlt wird: eine Full-Range-v2-artige Position mit demselben Kapital von $6.523,69 hätte Lv2=V/(2P)=59.55L_{v2} = V/(2\sqrt{P}) = 59.55 — die konzentrierte Position quotiert 12,4-mal mehr Tiefe pro Tick und verdient (solange sie in der Range ist) Fees mit der 12,4-fachen Rate pro Dollar Kapital.

Fee-Buchhaltung: feeGrowthGlobal und feeGrowthInside

Fees in v3 werden nicht in die Position hineinkomponiert (ein bewusster Bruch mit v2, wo Fees in k\sqrt{k} reinvestiert wurden). Sie akkumulieren separat, pro Token, und die Buchhaltung ist ein kleines Meisterwerk O(1)-fähiger Buchführung, das zu verstehen sich lohnt, weil jede LP-Analytics-Pipeline sie neu implementiert.

Der Pool führt zwei globale Akkumulatoren, feeGrowthGlobal0X128 und feeGrowthGlobal1X128: kumulative Fees pro Einheit Liquidität seit Pool-Erstellung, im Q128.128-Festkommaformat. Bei jedem Swap wird der Fee-Betrag (aus dem eingezahlten Token entnommen) durch das aktuell in der Range aktive LL geteilt und dem Akkumulator hinzugefügt. Dies ist der Mechanismus hinter der Kardinalregel der v3-Ökonomie: Fees fließen nur an Liquidität, die zum Zeitpunkt des Swaps in der Range aktiv ist. Positionen außerhalb der Range sind nicht bloß inaktives Inventar — sie verdienen exakt null, solange sie inaktiv sind.

Um den korrekten Anteil des globalen Wachstums einer endlichen Range zuzuordnen, speichert jeder initialisierte Tick ii feeGrowthOutside0/1X128 — das Fee-Wachstum, das auf der anderen Seite des Ticks relativ zum aktuellen Preis stattfand (der Wert kehrt seine Interpretation bei jeder Tick-Überquerung um, was das Schema O(1) macht). Für eine Position auf [i,iu][i_\ell, i_u] mit aktuellem Tick ici_c gilt dann:

fbelow(i)={fo(i)icifgfo(i)ic<ifabove(iu)={fo(iu)ic<iufgfo(iu)iciuf_{\text{below}}(i_\ell) = \begin{cases} f_o(i_\ell) & i_c \ge i_\ell \\ f_g - f_o(i_\ell) & i_c < i_\ell \end{cases} \qquad f_{\text{above}}(i_u) = \begin{cases} f_o(i_u) & i_c < i_u \\ f_g - f_o(i_u) & i_c \ge i_u \end{cases} finside=fgfbelow(i)fabove(iu)f_{\text{inside}} = f_g - f_{\text{below}}(i_\ell) - f_{\text{above}}(i_u)

Jede Position speichert einen Snapshot feeGrowthInsideLast, und nicht abgeholte Fees sind schlicht

fees owed=Lfinsidenowfinsidelast2128\text{fees owed} = L \cdot \frac{f_{\text{inside}}^{\text{now}} - f_{\text{inside}}^{\text{last}}}{2^{128}}

der träge aktualisiert wird, sobald die Position berührt wird. Zwei praktische Konsequenzen: Erstens sind feeGrowthInside-Deltas der einzige ehrliche Weg, das Fee-Einkommen einer Position zu messen — Pool-Level-Volumen zu erfassen und nach eigenem Anteil am TVL zu prorata-teilen, geht immer dann schief, wenn der Preis um die Range-Grenzen herum wandert. Zweitens: Weil Fees als tokensOwed liegen bleiben statt zu komponieren, haben realisierte LP-Renditen einen Cash-Drag-Term, den v2 nicht kannte; Auto-Compounding-Vaults existieren genau deshalb, um dies wegzuarbitrieren (abzüglich ihrer eigenen Fee).

Diagramm von feeGrowthGlobal, feeGrowthOutside an den Range-Grenz-Ticks und der feeGrowthInside-Subtraktion

Der Payoff: ein Short Straddle, für dessen Halten man bezahlt wird (vielleicht)

Innerhalb der Range ist der Positionswert als Funktion des Preises

V(P)=x(P)P+y(P)=L(2PpaPpb)V(P) = x(P) \cdot P + y(P) = L\left(2\sqrt{P} - \sqrt{p_a} - \frac{P}{\sqrt{p_b}}\right)

Konkav in PP — der Term 2LP2L\sqrt{P} ist die ganze Geschichte. Außerhalb der Range wird er linear: Steigung x(pa)x(p_a) unterhalb (man ist long einen festen ETH-Bestand), Steigung 0 oberhalb (man ist flach in USDC). Rechnen wir unser Beispiel über die Range durch und vergleichen es mit dem simplen Halten der eingezahlten Tokens:

PP (USDC/ETH) LP-Wert HODL-Wert Divergenz
2500 5.716,68 6.023,69 −307,02
2750 6.200,4 6.273,69 −73,3
3000 6.523,69 6.523,69 0
3250 6.706,3 6.773,69 −67,4
3500 6.764,06 7.023,69 −259,63

Der LP unterperformt HODL in beide Richtungen und erreicht Gleichstand nur beim Minting-Preis. Gedeckelte Aufwärtsseite, verstärkte Partizipation an der Abwärtsseite, maximaler relativer Wert am Strike... das ist der Payoff eines Short Straddle (genauer, sobald die linearen Enden gegen die HODL-Benchmark verrechnet werden: eine Short-Position in einem Streifen von Optionen mit Strikes über [pa,pb][p_a, p_b]). Der Fee-Strom ist die Prämie. Die Range zu konzentrieren bedeutet, engere Strikes zu wählen: mehr Prämie pro Zeiteinheit, solange man in der Range ist, dafür schnellere und tiefere Divergenz, wenn sich der Preis bewegt. Jeder v3-LP ist ein Short-Volatility-Trader, ob er es so gewählt hat oder nicht — das On-Chain-Pendant zum Inventory-Risk-Trade-off, den Avellaneda–Stoikov für Orderbuch-Market-Maker formalisieren, wobei die Range-Breite die Rolle des quotierten Spreads übernimmt.

LP-Positionswert im Vergleich zu HODL über die Preisrange, mit dem konkaven, Short-Straddle-artigen Payoff

Der traditionelle Name für die Lücke in dieser Tabelle ist Impermanent (Divergence) Loss, aber IL-versus-HODL ist eine fehlerhafte Benchmark: Sie vermengt den Verlust, den man als Liquidity Provider erlitten hat, mit dem Marktrisiko, das man ohnehin getragen hätte. Die schärfere Zerlegung stammt von Milionis, Moallemi, Roughgarden und Zhang (2022), "Automated Market Making and Loss-Versus-Rebalancing" (arXiv:2208.06046). Man vergleicht den LP nicht mit HODL, sondern mit einem Rebalancing-Portfolio, das zu jedem Zeitpunkt dieselben Token-Mengen wie der Pool hält — aber zum reibungsfreien externen Marktpreis handelt statt gegen Arbitrageure. Die Differenz ist LVR ("Lever"): die Komponente des LP-P&L, die reine Adverse Selection ist, gezahlt an Arbitrageure, die die veralteten Quotes des AMM nach jeder Preisbewegung abgreifen. Unter einer geometrischen Brown'schen Preisbewegung mit Volatilität σ\sigma akkumuliert LVR mit der instantanen Rate

(σ,P)=σ2P22x(P)\ell(\sigma, P) = \frac{\sigma^2 P^2}{2}\,|x'(P)|

— eine "Black-Scholes-Formel für AMMs", wie die Autoren es formulieren, mit x(P)|x'(P)| als marginaler Tiefe des Pools beim aktuellen Preis. Für den Full-Range-Constant-Product-Pool kollabiert dies zum berühmten σ28\frac{\sigma^2}{8} des Poolwerts pro Zeiteinheit: bei 5% täglicher Volatilität bluten grob 3,1 bp des Pools jeden Tag an Arbitrageure ab, Fees hin oder her. Konzentration multipliziert x(P)|x'(P)| — unser 12,4×-Tiefenbeispiel von oben ist gleichzeitig eine ~12,4×-LVR-Maschine, solange man in der Range ist. Fees müssen dieses Ausbluten überholen, damit die Position +EV ist, und LVR ist (anders als "IL") eine hedgebare, vorhersehbare laufende Kostengröße — das macht sie zur richtigen Rechnungseinheit. Die vollständige Profitabilitätsrechnung — Fee-APR versus LVR versus realisierte Volatilität, wann LPing eine delta-gehedgte Short-Options-Position schlägt — ist Thema des kommenden Deep Dive zu Impermanent Loss und LVR, und die Hedging-Mechanik erhält ihre eigene Behandlung in v3-LP-Strategien und Hedging.

Eine weitere Kostenebene liegt eine Schicht tiefer: Weil AMM-Quotes nur aktualisiert werden, wenn jemand handelt, ist jede LP-Position auch den Mempool-Spielen ausgesetzt, die um diese Trades herum gespielt werden — Sandwich-Angriffe gegen die Swapper, die Ihre Fees zahlen, und Arbitrage-Bundles, die Ihren LVR Block für Block realisieren. Dieses Ökosystem wird in dem Artikel zu MEV und Sandwich-Attacken kartiert.

Pool-Zustand On-Chain auslesen

Alles oben Beschriebene lässt sich anhand von drei günstigen Aufrufen des Pool-Contracts beobachten.

slot0() packt den heißen Zustand in einen einzigen Storage-Slot: sqrtPriceX96, den aktuellen tick, Oracle-Observation-Indizes und die Protocol-Fee-/Lock-Flags. liquidity() liefert das aktuelle in der Range aktive aggregierte LL — man beachte, dass dies nicht TVL ist; es ist der beim aktuellen Tick aktive Tiefenparameter, der diskontinuierlich springt, sobald der Preis einen initialisierten Tick überquert. ticks(int24) liefert Zustand pro Tick: liquidityGross (gesamtes LL, das den Tick referenziert), liquidityNet (vorzeichenbehaftetes LL, das beim Überqueren von links nach rechts hinzugefügt wird) und die feeGrowthOutside-Akkumulatoren. Iteriert man über liquidityNet bei initialisierten Ticks (die tickBitmap verrät, welche existieren, ohne alle 1,7 Millionen zu scannen), lässt sich das vollständige Tiefenprofil L(i)L(i) rekonstruieren — Ihr Orderbuch-Snapshot.

from web3 import Web3

w3 = Web3(Web3.HTTPProvider(RPC_URL))
pool = w3.eth.contract(address=POOL, abi=POOL_ABI)  # USDC/WETH 0.05%

sqrt_price_x96, tick, *_ = pool.functions.slot0().call()
L = pool.functions.liquidity().call()

raw_price = (sqrt_price_x96 / 2**96) ** 2          # token1/token0, base units
eth_usdc  = 1 / (raw_price * 10**(18 - 6))          # human USDC per ETH
depth_1tick = L * (1.0001**0.5 - 1) * (sqrt_price_x96 / 2**96)

Positionen selbst leben an zwei Orten. Der Kern-Pool indiziert sie über (owner, tickLower, tickUpper) — ein aggregierter Slot pro Owner-Range-Tripel. Retail-Nutzer und die meisten Fonds minten stattdessen über den Periphery-Contract NonfungiblePositionManager (Mainnet: 0xC36442b4a4522E871399CD717aBDD847Ab11FE88), der jede Position in ein ERC-721-NFT verpackt und positions(tokenId) bereitstellt, das das vollständige Tupel liefert: Tokens, Fee-Stufe, Range, LL, feeGrowthInsideLast und tokensOwed. Für das Portfolio-Monitoring liefert dieser eine Aufruf plus das aktuelle feeGrowthInside des Pools (neu berechnet aus ticks() wie im vorherigen Abschnitt) Mark-to-Market-Wert und aufgelaufene Fees, ohne einen Indexer anzufassen — für alles Historische braucht man jedoch Swap-Event-Replay oder einen Subgraph, weil Fee-Wachstum pfadabhängig ist und die Chain nur die aktuellen Akkumulatoren speichert.

Zwei operative Details, die man vor dem Kapitaleinsatz verinnerlichen sollte. Erstens quantisiert das Tick-Spacing den Strategieraum: auf der 0,05%-Stufe kann man 10-bp-breite Ranges platzieren und etwas laufen lassen, das einer echten Limit-Order sehr nahekommt (knapp über/unter Spot minten, die Fee kassieren, nach der Überquerung burnen — das Whitepaper rahmt diesen "Range-Order"-Anwendungsfall explizit), während auf der 1%-Stufe die Mindest-Range ~2% breit ist und die LOB-Analogie grob wird. Zweitens ist eine Range-Order eine Limit-Order ohne Storno-Priorität: Überquert der Preis Ihre Range und kehrt zurück, machen Sie einen Roundtrip mit dem Inventar und geben den Vorteil wieder her (behalten aber die Fees). Passive Ranges sind Quotes, die man nicht zurückziehen kann — genau deshalb ist die Frage nach der Rebalancing-Frequenz, und die LVR-Linse zu ihrer Beantwortung, so wichtig.

Wo das hinführt

Der v3-Stack, komprimiert: Preise sind Ticks auf einem 1,0001-geometrischen Raster; Tiefe ist LL, umrechenbar in Token-Beträge durch nichts weiter als Differenzen von Quadratwurzeln; Fees sind ein Akkumulator pro LL, den man über die Grenzen der eigenen Range hinweg differenziert; und die resultierende Position ist eine Short-Vol-Range-Order, deren laufende Kosten einen Namen, eine Formel und eine arXiv-Nummer haben. Mit diesen Primitiven in der Hand werden die interessanten Fragen quantitativ: wie breit, wie oft rebalancieren, und ob Fees den LVR für einen gegebenen Pool und ein gegebenes Regime ausgleichen — genau dorthin führt diese Serie als Nächstes.

Referenzen

  • Adams, H., Zinsmeister, N., Salem, M., Keefer, R., Robinson, D. (2021). Uniswap v3 Core (Whitepaper).
  • Uniswap-v3-Kernbibliotheken: TickMath.sol, SqrtPriceMath.sol, Position.sol, Tick.sol.
  • Milionis, J., Moallemi, C., Roughgarden, T., Zhang, A. L. (2022). Automated Market Making and Loss-Versus-Rebalancing. arXiv:2208.06046.
blog.disclaimer

Authors

Eugen Soloviov
Eugen Soloviov

Trading-systems engineer

Trading-systems engineer building bots since 2017: cross-exchange arbitrage (connected up to 30 venues), cointegration-based pairs arbitrage across spot and futures, scalping, news and sentiment-driven strategies, trend algorithms, and portfolio management and balancing algorithms. Also builds sub-millisecond order execution, big-data warehouses, backtesting engines, AI agents, and trading interfaces (incl. open-source profitmaker.cc). Stack: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, architecture.

Newsletter

Dem Markt einen Schritt voraus

Abonniere unseren Newsletter für exklusive KI-Trading-Einblicke, Marktanalysen und Plattform-Updates.

Wir respektieren deine Privatsphäre. Jederzeit abbestellbar.