🎁SDEX v2 proposal & algorithm
Changes to the SDEX reward algorithm
WT prop
Draft proposal for improving the AQUA SDEX rewards, to better deal with wash trading, and provide a fairer rewards distribution to traders that provide liquidity to the SDEX.
The purpose:
**The purpose of the AQUA SDEX orderbook rewards program is to reward and incentivize traders to add liquidity to the orderbook, so that everyone who wants to trade (especially swap) on the SDEX, gets a fair price, and can trade higher volume without losing a lot of value to slippage. Instead of fully fulfilling this purpose, the current rewards program rewards a massive share of rewards towards the Wash Traders (WTs) that generate the highest (fake) volume, and contribute almost no liquidity to the orderbook. This is a highly technical problem, requiring a highly technical solution, but I here suggest an SDEX rewards model that gives more AQUA rewards to the traders that contribute liquidity to the orderbook, instead of those providing fake volume. But this new model also needs to be resistant against other forms of orderbook manipulation, such as Spoofing (someone placing huge limit orders to manipulate the orderbook, with the intention to cancel the order before it gets fulfilled, so that they also provide very little real liquidity).
Recap of the current situation:
****AQUA is rewarded hourly for selected markets to individual wallets, according to their share of the total trading volume that has been placed on that market as limit orders, waiting for at least 12 blocks (ca 1 minute) before being fulfilled. Here, I’m assuming 200000 AQUA is paid out every day to traders in different markets (although this is not the case anymore).
TotalLimitVolume = total traded volume (of limit orders that has waited for at least 12 blocks) in the last hour.
TraderLimitVolume = a trader's total traded volume (of limit orders that has waited for at least 12 blocks) in the last hour.
TraderVolumeShare = TraderLimitVolume/TotalLimitVolume
TraderHourlyReward = (200000/24 AQUA) x TraderVolumeShare
So if a trader trades 10% of the total hourly volume, they receive 10% of the 8333 AQUA that gets distributed that hour, meaning 833 AQUA.
The problem:
****This is being exploited by WTs such as these 4 wallets:
https://stellar.expert/explorer/public/account/GDNGSSIA4YRKGANIZLQYIEK2MAKG2PBOGHLRLQMCQWUVRJAKVX4XLV5X (yUSDC/USDC)
https://stellar.expert/explorer/public/account/GDH2SSELTAXXYXZC5NNM7MIJGYZUONNWW3ZXNVUS5YJIZ4NVGMHHO2SO (yUSDC/USDC)
These use two wallets that alternate between placing large limit orders at the edge of the spread (difference between lowest limit sell order and highest limit buy order in the orderbook), which the other wallet buys after 1 minute. Therefore, these WTs generate huge fake volumes, taking almost all the AQUA rewards. Meanwhile, they provide almost no liquidity to the market, since the orders are waiting for only the minimum required time, and are highly unlikely to be matched by anyone other than their own bots. These WTs trade profitably with almost no risk, since even if someone else fulfils their orders, the liquidity caused by legit market-makers (MM) enables the WTs to buy back their order with a minuscule loss, compared to the outsized AQUA rewards they receive. WTs like these, also operate not only on the 1-to-1 pegged markets like yXLM/XLM and yUSDC/USDC, but also on other non-pegged markets, even though they are not as blatantly obvious as the 4 above.
Because the rewards program is focused entirely on trading volume, it is very hard for legit MMs (such as people running a Kelp bot
https://www.stellar.org/developers-blog/kelp-gui-your-first-automated-trading-bot?locale=en
) to out-compete these WTs. Real marker-makers have to take a real risk of loss by providing real liquidity, and since the real trading volume caused by regular traders is minuscule compared to these WTs, most of the incentive for MMs is gone.
So, to conclude, the current AQUA SDEX orderbook rewards program heavily incentivizes WTs providing fake volume and almost no liquidity, in favor of liquidity-providing by MM. Therefore, we should change the rewards program to more heavily incentivize providing actual liquidity, in addition to volume.
The proposed solution:
****(This solution is definitely still not yet complete and is very much subject to discussion and change, but hopefully the description is good enough to be understandable, and can serve as a guideline towards a better solution.) The goal of this proposed change in the rewards program, is to refocus the AQUA rewards towards the MMs that provide actual liquidity, rather than the WTs. This is done by replacing the previous TraderVolumeShare by a TraderLiquidityShare.
TraderHourlyReward = (200000/24 AQUA) x TraderLiquidityShare
A trader can achieve a high TraderLiquidityShare by holding a high volume in limit orders in the orderbook very close to the current price, for as long as possible. Compared to previously, orders that are not fulfilled before being cancelled, will still count towards TraderLiquidityShare, but fulfilled orders count more than orders that get cancelled. Volume placed in orders closer to the spread contributes more liquidity than orders placed far away from the spread. Therefore, orders placed at a competitive price, will contribute far more towards TraderLiquidityShare, than orders placed far away. These three requirements: 1) to hold an order for a long time, 2) placing orders close to the spread, and 3) fulfilled orders count more than cancelled order, are intended to encourage MMs to close the spread down towards 0%, while discouraging WTs and Spoofers. To calculate a trader’s TraderLiquidityShare according to these rules, I will here suggest a method, though this is very much up to discussion for the AQUA community, and there might realistically be technical limits to how the AQUA team can actually implement this method.
Definitions:
• TraderLiquidityShare = TraderWeightedLiquidity / TotalWeightedLiquidity = the share of liquidity provided by an individual trader compared to the total liquidity provided by all traders in a market.
• TraderWeightedLiquidity = the summed total of weighted liquidity provided by all limit orders of an individual trader in a market over a period of time.
• TotalWeightedLiquidity = the summed total of weighted liquidity provided by all limit orders in a market over a period of time.
• OrderPrice = the price of an order.
• OrderVolume = the volume of an order.
• LowestSellPrice = the price of the lowest order on the sell-side of the orderbook.
• HighestBuyPrice = the price of the highest order on the buy-side of the orderbook.
• TimeWeight = duration of a time interval that an order remains in the orderbook.
• SpreadWeight = the weight of an order depending on its OrderPrice relative to LowestSellPrice or HighestBuyPrice.
• FulfilledWeight = 2 if an order gets fulfilled, between 1 and 2 if the order is partially fulfilled, else FulfilledWeight = 1
• OrderWeightedLiquidity = OrderVolume x TimeWeight x SpreadWeight x FulfilledWeight = the weighted liquidity of an order during a time interval
Method:
First, is one of the bigger challenges with this proposal, the AQUA rewards engine need to know the state of the orderbook over time, including keeping track of which wallets have placed which orders.
This is used to count for each wallet a contribution towards the wallet’s TraderLiquidityShare, for every order that a wallet places in the orderbook, for as long as the order remains in the orderbook.
For a time interval, e.g. 5 minutes, the prices of the foremost orders, LowestSellPrice and HighestBuyPrice, are determined (e.g. by weighted averaging the price of the foremost orders during the time interval), and each order is given a TimeWeight for how long it remains in the orderbook during the interval.
◦ Example: an order remains in the orderbook for the entire time interval, it therefore gets a TimeWeight = 5/5 = 1 for this interval. Another order from a WT remains in the orderbook for only one minute, and get a TimeWeight = 1/5 = 0.2
◦ Example: in a 5 min interval, for the first minute the LowestSellPrice is 0.03, and for the remaining four minutes the LowestSellPrice is 0.031. The LowestSellPrice for the entire interval is then (0.03 x 1 + 0.031 x 4) / 5 = 0.0308.
◦ It is possible the time interval should be adjusted according to the liquidity/volume/activity of a market. For example, a market with a high liquidity and frequently changing orderbook might need a short time interval of 1 minute to maintain up-to-date LowestSellPrice and HighestBuyPrice, while a low liquidity market with only a few trades per day might only need a time interval of 60 minutes.
During the same time interval in 3., for each order in the orderbook, its SpreadWeight is calculated from the OrderPrice relative to the LowestSellPrice or HighestBuyPrice, by the formula:
◦ SpreadWeight = (LowestSellPrice/OrderPrice)^6 (sell-side of the orderbook)
◦ SpreadWeight = (OrderPrice/HighestBuyPrice)^6 (buy-side of the orderbook)
◦ Example: LowestSellPrice is at a price of 0.03, another order sell order is placed above it at a price of 0.04, and another at 0.06. The lowest sell order at LowestSellPrice then has SpreadWeight = (0.03/0.03)^6 = 1, the second order has a SpreadWeight = (0.03/0.04)^6 = 0.1778, and for the third order SpreadWeight = (0.03/0.06)^6 = 0.015625.
◦ This means that as an order gets placed further away from the spread, it quickly loses its weighting for the rewards formula. Yet, these rewards become not only for trading bots, but it is still possible for casual traders to provide liquidity by placing orders a bit further from the spread, which might remain a long time in the orderbook, and get a small reward for it. Also, because LowestSellPrice and HighestBuyPrice are weighted averages of the time interval, it is possible for traders to briefly place orders in front of the HighestBuyPrice and LowestSellPrice, which would give a SpreadWeight higher than 1.
◦ I have here set an exponent for SpreadWeight of 6, which would quite aggressively encourage MM bots to compete to close the spread towards 0%. However, an even higher exponent would give a stronger incentive for MM bots, while making it harder for casual traders. And a lower exponent would make it easier for casual traders to get some rewards, but the spread and liquidity would likely get worse. It might be that the exponent should be adjusted according to volume/liquidity of markets, with a higher exponent for higher liquidity markets, and a lower exponent for lower liquidity markets.
FulfilledWeight of an order during a time interval is 1 + the amount of OrderVolume that gets fulfilled by another trader during the time interval. This is intended to further incentivise traders to place orders close to the spread, where they have a high chance of being fulfilled. ◦ Example: an order gets that completely fulfilled during the time interval has FulfilledWeight = 2, and an order that get 30% filled has FulfilledWeight = 1.3, an order that remains untouched or canceled during the time interval has FulfilledWeight = 1
Finally, the OrderWeightedLiquidity of an order during a time interval is calculated as OrderWeightedLiquidity = OrderVolume x TimeWeight x SpreadWeight x FulfilledWeight
TraderWeightedLiquidity for a trader over an hour can now be calculated by adding together all the OrderWeightedLiquidity of each of the trader’s orders during each of the time periods.
TraderLiquidityShare of each trader during a period of an hour can now be calculated, by TraderLiquidityShare = TraderWeightedLiquidity / TotalWeightedLiquidity.
Let’s now have a look at how this method compares to the current rewards program by looking at a scenario over 1 hour of a hypothetical AQUA/XLM market, with three traders who have placed limit orders in the orderbook. For the first half-hour (six 5-min intervals), the HighestBuyPrice on the buy-side of the orderbook remains at 0.03 XLM/AQUA, while in the second half-hour, the HighestBuyPrice stays at 0.029 XLM/AQUA.
1st: a Casual Trader has placed a buy order of 5000 XLM at a price of 0.025, which remains untouched for the entire hour. 2nd: a Wash Trader places buy orders of 200000 XLM, three times, that remains in the orderbook close to the edge of the for spread for 1 minute, before the Wash Trader’s other wallet sells into its own orders. The Wash Trader therefore generates a trading volume of 3 x 200000 = 600000 XLM. 3rd: an aggressive Market Maker maintains 20000 XLM buy orders close to the edge of the spread for the entire hour, which gets fulfilled 12 times, once per 5 min interval. The Market Maker generates a trading volume of 12 x 20000 = 240000 XLM.
According to the current rewards model, which only takes trading volume into account, the Casual Trader would get no rewards, since the order remains untouched. The Wash Trader generates a trading 600000/840000 = 71.4% of the trading volume, and thus receives 5952 AQUA. The Market Maker generates 120000/720000 = 28.6% of the trading volume, and receives 2381 AQUA.
According to the proposed rewards model, which mainly takes into account hold liquidity in the orderbook, the rewards get distributed very differently. 1st: The Casual Trader’s order generates a OrderWeightedLiquidity for every 5-min interval, even though the order never gets fulfilled. In the first 6 intervals the order has a SpreadWeight = (0.025/0.03)^6 = 0.334898, and in the last 6 intervals the order has a SpreadWeight = (0.025/0.029)^6 = 0.410442. For each of the 6 first intervals, the order generates OrderWeightedLiquidity = OrderVolume x TimeWeight x SpreadWeight x FulfilledWeight = 5000 x 1 x 0.334898 x 1 = 1674.49, and the 6 last intervals, OrderWeightedLiquidity = 5000 x 1 x 0.410442 x 1 = 2052.21. In total, the Casual Trader generates TraderWeightedLiquidity = 6 x 1674.49 + 6 x 2052.21 = 22360.2.
2nd: The Wash Trader only has orders in the orderbook for 1 minute before they get fulfilled at the same price at HighestBuyPrice, twice during the first half hour while HighestBuyPrice is 0.03, and once in the second half hour, when HighestBuyPrice is 0.029. Both of these first two orders then generate OrderWeightedLiquidity = 200000 x 0.2 x (0.03/0.03)^6 x 2 = 80000, and for the last order OrderWeightedLiquidity = 200000 x 0.2 x (0.029/0.029)^6 x 2 = 80000. In total TraderWeightedLiquidity = 3 x 80000 = 240000. 3rd: The Market Maker competitively keeps 20000 XLM orders at the front of the orderbook for the entire hour, and fulfils one order in each time interval. In each time interval the 20000 XLM order then generates OrderWeightedLiquidity = 20000 x 1 x (0.03/0.03)^6 x 2 = 40000. In total TraderWeightedLiquidity = 12 x 40000 = 480000.
Then finally, we can calulate for each trader TraderLiquidityShare = TraderWeightedLiquidity / TotalWeightedLiquidity, where TotalWeightedLiquidity = 240000 + 480000 + 22360.2 = 742360.2. 1. Casual Trader: TraderLiquidityShare = 22360.2/742360.2 = 3.012% or 251 AQUA 2. Wash Trader: TraderLiquidityShare = 240000/742360.2 = 32.33% or 2694 AQUA 3. Market Maker: TraderLiquidityShare = 480000/742360.2 = 64.66% or 5388 AQUA
To be discussed and determined: Does the new model also need a minimum time of 12 blocks (1 minute) for being counted towards TraderLiquidityShare? A minimum time limit can now possibly be counterproductive. On one hand it likely still reduces wash trading, but on the other hand, it is normal for professional high-performance MM bots to place orders for very brief periods of time. And anyways, a trader will get a much higher TraderLiquidityShare by keeping orders in the orderbook for longer than 1 minute. Realistically, there should be a minimum threshold for TraderLiquidityShare before a trader receives a payout, to avoid needing to reward a large number of tiny rewards, like 0.0000001 AQUA every hour to an account that has placed a buy order with 100 XLM at 0.0000001 XLM/AQUA, while the price is 0.03 XLM/AQUA. There are likely some edge cases that can be challenging to calculate exactly, such as orders that get moved around changing prices and volumes very rapidly (within seconds).
Who will benefit from this proposed change: Foremost, MMs benefit from this, who need to take a real risk of loss to provide liquidity. (Disclaimer: I am personally in this category, running a Kelp-like MM bot). A higher reward to MMs means they can afford to take a higher risk, by providing more liquidity to the SDEX. A high MM reward even enables MMs to compete aggressively, trading at a slight loss, as long as the rewards are higher than the loss. Additionally, also casual traders, who maybe look at the SDEX for a few minutes a day and places only a single limit order, can benefit from this proposed rewards system. Even though the current AQUA SDEX rewards program has already succeeded in increasing the liquidity on the SDEX, this proposed will likely further push orderbook spreads down closer to 0%, and improve the SDEX liquidity possibly approaching that of top centralized exchanges, like Binance. Therefore, this proposed change benefits all traders on the SDEX, and is likely to help the entire Stellar ecosystem, by providing a better trading experience and fairer price to both casual and advanced traders alike.
Who will lose from this proposed change: Essentially only WTs will lose from this proposal, and they are anyways parasites that suck value out of the SDEX, so, good riddance.
Last updated