The last lesson needed two venues to make money: ETH was cheap on one DEX, dear on another, and you walked the gap. But here’s the unsettling part — you don’t even need a second exchange. A single DEX, with three perfectly ordinary pools, can quietly contradict itself. Cycle a coin through the right loop and you come out the other side holding more of what you started with. No second venue. No price feed disagreeing. Just three pools that, multiplied together, don’t quite agree on what a dollar is worth.
Closing a loop
Before you read — take a guess
You start with 100,000 USDC, swap it for ETH, swap that ETH for DAI, then swap the DAI back to USDC — all on one DEX. Can you end up with MORE than 100,000 USDC?
Classic foreign-exchange traders have hunted this for a century. Imagine a money-changer’s window quoting dollars, euros, and yen. Suppose you can turn 1 dollar into 0.9 euros, each euro into 160 yen, and each 144 yen back into 1 dollar. Walk that triangle — dollar → euro → yen → dollar — and if the three rates don’t line up, you finish the loop with more dollars than you began with, without ever predicting which way a currency will move. You just closed a loop that was internally inconsistent.
Triangular arbitrage is that exact trick, on-chain: cycle a starting asset through three (or more) pools and end with more of the asset you began with. The canonical loop is:
Three swaps, three pools, back to where you started — ideally heavier. The beautiful (and slightly disturbing) part: no second venue is required. One DEX holding a USDC/ETH pool, an ETH/DAI pool, and a DAI/USDC pool is fully capable of being internally inconsistent, because each pool sets its price off its own reserves with nobody enforcing agreement between them.
One venue, still inconsistent
Cross-DEX arbitrage exploits two exchanges disagreeing about one pair. Triangular arbitrage exploits one exchange disagreeing with itself across three pairs. No external price, no second venue — just a loop whose links don’t quite multiply to 1.
Match each idea to what it means in a triangular loop.
Pick a term, then click its definition.
When it matters
Most people picture arbitrage as “same thing, two prices.” Triangular arbitrage breaks that mental model: the mispricing isn’t in any one pair, it’s distributed around a loop. Bots that only scan pairs for cross-venue gaps miss it entirely. To catch it you have to think in cycles, not pairs — which is why triangular scanning is a distinct, and lucrative, corner of the MEV world.
The no-arbitrage condition
Before you read — take a guess
You multiply the three exchange rates around a loop and get exactly 1.000. What does that tell you?
Here’s the whole game in one expression. Walk the loop and multiply the three exchange rates you receive at each leg. Call them , , (each one is “how much of the next asset you get per unit of the current asset”). In a perfectly consistent market:
That’s the no-arbitrage condition. A full round trip multiplies your starting amount by exactly that product. If the rates are mutually consistent, the product is 1, and you end with precisely what you began with — a pointless lap.
Now the two ways it can break:
- If , cycling this direction multiplies your stake by more than 1 — profit. Run the loop USDC → ETH → DAI → USDC.
- If , this direction loses money — but the reverse loop has product , so you cycle the other way instead.
So the product isn’t just a yes/no signal; its size tells you the edge and its position relative to 1 tells you the direction. A product of 1.005 is a 0.5% edge clockwise; a product of 0.995 is a 0.5% edge counter-clockwise.
Direction is half the trade
Spotting “the product isn’t 1” is useless if you run the loop the wrong way — you’d pay the inefficiency instead of collecting it. Always check whether the product is above or below 1 and cycle in the direction whose product exceeds 1. Below 1 means the profitable loop is the reverse.
Fill in the no-arbitrage logic.
Choose the correct option for each blank and check.
In equilibrium the product of the three loop rates equals . If that product comes out greater than 1, cycling that direction is ; if it comes out less than 1, you cycle the .
When it matters
This single inequality is the entire detector. A triangular-arb bot doesn’t reason about ETH “going up” — it just multiplies rates around every loop it can find and pounces whenever a product strays from 1. The further from 1, the bigger the prize and the harder bots race to grab it. Equilibrium () is the boring state the whole ecosystem is constantly dragging the market back toward.
A worked loop
Before you read — take a guess
USDC→ETH gives you ETH at 2000 USDC per ETH; ETH→DAI gives 2010 DAI per ETH; DAI→USDC is 1.00. Roughly what's the loop's edge?
Let’s make it concrete with the canonical numbers. Our one DEX quotes:
| Leg | Asset in | Rate | Asset out |
|---|---|---|---|
| 1 | USDC | 2000 USDC per ETH | ETH |
| 2 | ETH | 2010 DAI per ETH | DAI |
| 3 | DAI | 1.00 USDC per DAI | USDC |
Multiply the loop. Going in, 1 USDC buys ETH; that ETH fetches 2010 DAI each; each DAI fetches 1.00 USDC. The product of the rates around the loop is:
A product of 1.005 — a clean 0.5% edge, cycling in this direction. Now run real money through it, starting with 100,000 USDC:
| Step | You hold | Action | You get |
|---|---|---|---|
| Start | 100,000 USDC | — | — |
| Leg 1 | 100,000 USDC | buy ETH at 2000 | 100000 / 2000 = 50 ETH |
| Leg 2 | 50 ETH | sell ETH for DAI at 2010 | 50 × 2010 = 100,500 DAI |
| Leg 3 | 100,500 DAI | sell DAI for USDC at 1.00 | 100500 × 1.00 = 100,500 USDC |
You started with 100,000 USDC and finished with 100,500 USDC — a gross profit of about 500 USDC for three swaps and a single block of patience. Notice that’s exactly : the loop product is the multiplier on your stake.
Read the product as a multiplier
The loop product isn’t an abstract diagnostic — it’s literally the number your starting balance gets multiplied by. A product of 1.005 on 100,000 USDC means 100,500 USDC out. Want the profit? Stake times (product minus 1). Here: 100,000 × 0.005 = 500 USDC.
Sort each value from the worked loop into what it represents.
Place each item in the right group.
- 50 ETH after leg 1
- 2000 USDC per ETH
- 100,500 USDC at the end
- 100,500 DAI after leg 2
- 1.005 loop product
When it matters
This is the headline number — and, just like the cross-DEX “200 USDC spread,” it’s a lie of omission. The 500 USDC assumes every leg fills at the quoted rate no matter how much you push through. It won’t. The worked loop tells you an opportunity exists and which way to run it; it overstates what you actually pocket. The next section is where reality docks its cut.
Price impact shrinks the loop
Before you read — take a guess
Each leg of the loop is an AMM swap that moves its own pool against you. What does that do to the realized loop product compared to the naive 1.005?
Every leg of our loop is an AMM trade, and we already know what AMM trades do: they slide along the curve and move the pool’s price against the trader. Buy ETH on leg 1 and the USDC/ETH pool’s ETH price climbs — you pay more than 2000 for the later units. Sell that ETH on leg 2 and the ETH/DAI pool’s ETH price sinks — you collect less than 2010 on the later units. Each leg quietly underdelivers versus its headline rate.
So the realized loop product is below the naive 1.005, and the true profit is less than 500 USDC. The visual below shows a single leg doing this — sell a token into a pool and the spot price (the slope from the origin) bends the wrong way with every unit:
- X sold in
- 0
- Y received out
- 0
- Old spot price
- 2,000
- New spot price
- 2,000
Each leg of the triangle is an AMM swap. Push tokens into a pool and you slide along the x·y = k hyperbola: the reserve you add grows, the reserve you take shrinks, and the effective rate degrades with every unit. Stack three of these and the realized loop product drops below the naive 1.005.
Because the edge erodes as you scale, there’s an optimal cycle size — the very same hump from the last lesson. Trade too little and you leave the 0.5% mostly on the table; trade too much and price impact across all three legs claws the edge back, eventually dragging the realized product below 1 (a losing loop). Profit rises, peaks, and falls — and the peak depends on how deep the three pools are.
And don’t forget direction. Run the loop the wrong way and you start from product 0.995 — every leg compounds against you, so price impact isn’t your only problem; you were underwater before you pressed go. Always cycle the direction whose product exceeds 1.
The naive 500 is a ceiling, not a payout
The 500 USDC from the perfect-rate loop is the most you could ever extract, approached only in the limit of an infinitesimally small trade. Any real size you push through three pools books less. Size for the hump’s peak — not the biggest loop the reserves will swallow — and never run the loop whose product is below 1.
A bot finds a loop with naive product 1.005 (≈500 USDC on 100k). It fires the largest cycle the pools will accept. What most likely happens?
When it matters
This closes the arc from the last lesson cleanly: detection is a multiplication (is the product off 1?), but extraction is an optimization (where’s the hump, and which direction?). Triangular bots that only check whether a loop is mispriced — without solving for size and direction — routinely fire trades that look like free money and settle for crumbs, or losses, after three rounds of price impact and gas. The edge is real; capturing it is a sizing problem.
Recap
Big picture
Triangular arbitrage, in one map
- Triangular arbitrage
- The loop
- USDC to ETH to DAI to USDC
- One DEX, three pools
- End with more than you started
- No-arbitrage condition
- Product of three rates equals 1
- Above 1 cycle this way
- Below 1 cycle the other way
- Worked loop
- ETH at 2000 then 2010
- Product 1.005 a 0.5 percent edge
- 100000 in, 100500 out
- Price impact
- Each leg moves its pool against you
- Realized product below 1.005
- Optimal cycle size, a hump
- The loop
Triangular arbitrage — final check
What makes triangular arbitrage different from cross-DEX arbitrage?
Check your answer to continue.