Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Add minAverageSellPrice for LO #736

Merged
merged 10 commits into from
Oct 14, 2024
Merged

Conversation

jcompagni10
Copy link
Contributor

Adds a new field for placing a limit order MinAverageSellPrice. This provides more control for users when placing a limit order and resolves a number of edge cases. Currently, we use limitSellPrice to set the highest tick we try to trade through, and as part of a final check to make sure the average trade price for the entire trade is above the limitSellPrice. MinAverageSellPrice is a new optional parameter that allows the user to set a limit for the final aggregate/average price different from what is specified by the limitSellPrice.

@jcompagni10
Copy link
Contributor Author

@@ -243,6 +243,57 @@ func (s *DexTestSuite) TestPlaceLimitOrderInsufficientFunds() {
s.assertAliceLimitSellFails(err, "TokenA", 0, 10)
}

func (s *DexTestSuite) TestPlaceLimitOrderWithDustMinAvgPriceFails() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

world's smallest nit, but naming these
TestPlaceLimitOrderGTCWithDustMinAvgPriceFails
TestPlaceLimitOrderGTCWithDustMinAvgPrice
for consistency with the IOC tests

NicholasDotSol
NicholasDotSol previously approved these changes Sep 25, 2024
@jcompagni10
Copy link
Contributor Author

OrderType: types.LimitOrderType_GOOD_TIL_CANCELLED,
MinAverageSellPrice: &limitPrice,
})
s.ErrorIs(err, types.ErrLimitPriceNotSatisfied)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the error you are getting this place not because of the new logic, but because of the rounding nature. And thats why

  1. LimitPrice converted from price to tick to tick and back in a msgHandler, and in the code limitPrice lower than MinAverageSellPrice (despite the fact that you set them the same)
    006736936405289611298728644 < 0.006737
  2. On every swap iteration we check
		if limitPrice != nil && liq.Price().LT(*limitPrice) {
			break
		}

in other words we swap only and only if liq.Price>limitPrice

If every price > limitPrice, it's impossible the avg deal true price < limitPrice.
swap debug via 4 deposits:

price:  0.006738957688326027277211891
inAmount:  149 outAmount:  1
price:  0.006738283859940033273884502
inAmount:  149 outAmount:  1
price:  0.006737610098930140259858517
inAmount:  149 outAmount:  1
price:  0.006736936405289611298728644
inAmount:  1485 outAmount:  10

seems like only because of accumulated rounding errors we got realsellprice 13/1932=0.006728778467908902691511387 < limitPrice

I would suggest you to add two (at least) test cases, i mean and fail and success

  1. with bigger deposits and swaps amounts
  2. with a large spread between ticks to make the meaning of MinAverageSellPrice more obvious

Copy link
Contributor Author

@jcompagni10 jcompagni10 Oct 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the intended functionality. The goal was to be able to trade through a specific tick but tolerate a certain amount of price increase due to rounding.
There is another possible use case, which I believe you are describing where MinAverageSellPrice > LimitSellPrice. This would allow trading through a certain tick, but require the average price to be better than the last tick traded through. I will add some tests for this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see. I thought the "another possible use case" is the intended functionality :) Something like: "I want to sell my 10BTC for 500USDT and i do not care about highest and lowest price, i just want my average". Confused with the name. If you wanna just be tolerant to the rounding error, did you think about another approach, e.g. instead of 'MinAverageSellPrice', introduce the bool flag "beRoundingTolerant" and do not check at all truePrice if true, or may be roundingTollerance as offset ticks, i.e. the limit of rounding error which is allowed to bypass, in the nutshell the second approach is close to what you did, but it looks more intuitive for me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We went back and forth on this a lot. Just being ok with all rounding allows the possibility of front running, since there is no cap on how bad rounding can get. So it's better Passing an explicit value is much safer. Both use cases are valid. I will update docs to better explain the usage for this param

NewDeposit(0, 10, 70, 1),
NewDeposit(0, 10, 80, 1),
)
// THEN alice places IOC limitOrder with very low MinAveragePrice Fails
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With very high? (not "low")

OrderType: types.LimitOrderType_GOOD_TIL_CANCELLED,
MinAverageSellPrice: &limitPrice,
})
s.ErrorIs(err, types.ErrLimitPriceNotSatisfied)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see. I thought the "another possible use case" is the intended functionality :) Something like: "I want to sell my 10BTC for 500USDT and i do not care about highest and lowest price, i just want my average". Confused with the name. If you wanna just be tolerant to the rounding error, did you think about another approach, e.g. instead of 'MinAverageSellPrice', introduce the bool flag "beRoundingTolerant" and do not check at all truePrice if true, or may be roundingTollerance as offset ticks, i.e. the limit of rounding error which is allowed to bypass, in the nutshell the second approach is close to what you did, but it looks more intuitive for me.

@pr0n00gler pr0n00gler merged commit 3b94910 into main Oct 14, 2024
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants