v2-periphery icon indicating copy to clipboard operation
v2-periphery copied to clipboard

math error in computeProfitMaximizingTrade

Open Ivshti opened this issue 3 years ago • 7 comments

I was working out the math behind computeProfitMaximizingTrade and I discovered an issue there

assuming a, b are reserve0 and reserve1, the math in that function: sqrt(a*b*price/0.997) - a/0.997 differs from what I got by working it back from the spot price: (sqrt(a*b*price) - a)/0.997

Here's how I worked it out:

Starting with price = a / b, then applying the post-trade reserve amounts or price = (a+0.997x)/((a*b) / (a+0.997x)), assuming x is the input amount, we get (a+0.997x)**2 / (a*b)

After expanding that we get (sqrt(a*b*price) - a)/0.997 Screenshot_2021-08-09 Equations and systems solver - MATLAB solve(1)

It's likely that this wasn't noticed so far due to the relatively small impact of this error

Update: the correct formula for spot is actually (a+x)/(a*b / (a+0.997x)), but even with that I can't work it out to sqrt(a*b*price/0.997) - a/0.997 as it's in the contact

There's some other issues hinting that there may be a math bug: https://github.com/Uniswap/uniswap-v2-periphery/issues/100 https://github.com/Uniswap/uniswap-v2-periphery/issues/91 - none of them seem to explain the reasoning but price = (a+x)/(a*b / (a+0.997x)) is definitely not solved to x = sqrt(a*b*price/0.997) - a/0.997. When we disregard the fee, my method is consistent with the contract (getting x = sqrt(a*b*price) - a starting from price = (a+x)^2 / (a*b))

Ivshti avatar Aug 09 '21 07:08 Ivshti

Hello @Ivshti

It seems that you are right. In my case I do not make the function work in full detail. You don't end up calculating the best swap amount entry in Uniswap v2.

Do you know any other way to calculate the best entry amount for the pair?

alvarheras avatar Sep 03 '21 12:09 alvarheras

Hi @Ivshti

I checked the math here and it looks correct:

Let k = (1 - fee rate), i.e. 997/1000 in the implementation. The profit f(x) = - p1 / p0 * x + (k * r1 * x) / (r0 + k * x), where x is the input amount of tokenA (we assume tokenA is the input token), r0 and r1 are the amounts of tokenA and tokenB respectively, p0 and p1 are true prices for tokenA and tokenB respectively. The first term is the amount of tokenB we can purchase from the market using true prices, while the second term is the amount of tokenB we can swap for using Uniswap.

To maximize f(x), we will consider its derivative f'(x) = 0, which will result in x = sqrt(p0 / p1 * r0 * r1 / k) - r0 / k. This is the same as the one in the implementation.

Please let me know if I made any mistakes in the calculation.

dshe-certik avatar Sep 28 '21 19:09 dshe-certik

@dshe-certik why is k applied to the second term but not to the first? Also I'm not sure whether it has to apply to both the nominator and the denominator on the second term.

But please check my way of deriving the formula cause it starts from scratch and it uses the formulas on the Uniswap contracts themselves. Sorry for the confusing way I've written it, what matter is after "Update: the correct formula for spot is actually". Notice how in that case the k (fee multiplier) is not applied to one of the nominators ((a+x)/(a*b / (a+0.997x))) - the reason for this is that the WHOLE amount is actually added to the pool (we're talking v2 here only), so then the spot calculation DOES include the fee amount (0.003*input). This is how it works in practice - with computeProfitMaximizingTrade we want to calculate the amount such that we achieve a certain spot price (r0 / r1).

Just to make sure we're on the same page, the function takes p1 and p0 but they represent the target ratio between r0 and r1 - I call this spot price

Ivshti avatar Sep 29 '21 14:09 Ivshti

@Ivshti I think this function is designed to calculate the profit a transaction can make from the price difference between some market and Uniswap. For example, if I can purchase 1 tokenA with the price of 100 tokenB on some market, while I can swap 1 tokenA for 110 tokenB with Uniswap, then I would purchase tokenA on the market and sell it with Uniswap.

truePriceTokenA and truePriceTokenB are the prices of tokenA and tokenB on the market, rather than the spot prices on Uniswap. That's why k is not applied to the first term. Hope it answers your question.

dshe-certik avatar Oct 03 '21 21:10 dshe-certik

hi guys, I'm kind of new here. Sorry for the basic question. What is exactly truePriceTokenA? Because true prices can be from spot market or from another dex. What should I consider? Furthermore, how do I calculate it? Thank you in advnace.

djmoura avatar Oct 25 '22 20:10 djmoura

@djmoura True Price could be loosely read as the 'desired' price. The price the market should be moved to.

For example, let's say tokenA is worth 3 tokenB (1tokenA:3tokenB) to you, but is trading on Uniswap at 2 tokenB (1tokenA:2tokenB) market price. You would say the truePriceTokenA is 3, even though the price on Uniswap is 2. This formula calculates the amountIn on tokenB required to move the Uniswap market price to 3 (1tokenA:3tokenB).

afistfulofstraycathair avatar Nov 21 '22 23:11 afistfulofstraycathair

dshe-certik A very simple argument for why the current formula is wrong is evaluating it when b = a/price Trade amount should obviously be 0, but current formula gives:

sqrt(a*(a/price)*price/0.997)-a/0.997 = a(1/sqrt(0.997)-1/0.997)

putting k (=0.997) outside the sqrt yields the right evaluation

SergeKireev avatar Mar 23 '23 07:03 SergeKireev