Rewards are available for market makers automatically
Rewards are based on a scoring system that considers each makerâ€™s active limit orders every minute
Makers score quadratically more the closer they quote to the spread
Larger orders linearly increase scores
Quoting should be balanced because only the worst scoring side in a market contributes to score
A makerâ€™s scoring relative to other makers matters
Uptime is effectively considered quadratically
Note: This article reflects the rewards program as of March 15th, 2023. The program is continuously evolved, and the official documentation should be referred to for an up-to-date reference of calculations. Notably, single-sided liquidity rewards were released with Epoch 56. To do this, a simple modification was made to equation 4.
In this article, we will give an example-based introduction to the Polymarket Market Making Reward Program. We will start by providing an overview of â€śbinary tradingâ€ť on Polymarket to develop foundation knowledge. Then, we will dive into the rewards program with the aim of helping the reader develop an understanding of the program and ultimately ideas of how they can profitably earn rewards by providing liquidity on Polymarket. If you arenâ€™t familiar with Polymarketâ€™s underlying outcome token primitive, we suggest first reading our recent blog post â€śA Detailed Look at Polymarketâ€™s Binary Outcome Tokensâ€ť.
When you first begin trading on Polymarket, one thing you will notice is that when you place a limit order for an outcome (ie YES), a limit order also appears on the complementary outcomeâ€™s book (ie NO). The reason for this is that underlying binary markets is a single, â€śunifiedâ€ť book. When trading against this book, the view of the orders in it changes based on what outcome is being traded but the orders themselves are not mutated (and obviously canâ€™t be changed because they are signed objects). The view change is a simple inversion of bids and asks (bids become asks and asks become bids) and the prices are adjusted to be 1 - the complement bookâ€™s order price. All directions of trading (ie buy YES, buy NO, sell YES, sell NO) are supported by orders on this single underlying book. A few simple examples will help illustrate exactly how this works.
Letâ€™s consider the simplest case where both users are trading the same asset. Our first trader, Jack, places an order to buy 10 YES @$0.35. Our second trader, Jill, places an order to sell 10 YES @$0.35. The matching calculation for this example is simple, $3.50 will be transferred from Jack to Jill in exchange for her 10 shares.
Now, letâ€™s consider the case where the users both want to buy a complementary asset. Our first trader Jack again places an order to buy 10 YES @$0.35. This time, our second trader, Jill, is actually interested in the NO side and thus places an order to buy 10 NO @$0.65. At first glance it might seem that these orders are distinct, but recall that $1 can be split to 1 YES and 1 NO at anytime, thus we can actually calculate a match here. The matching calculation is as follows transfer $3.50 from Jack and $6.50 from Jill ($10 in total) and use it to mint 10 full conditional token sets (10 YES and 10 NO), then distribute 10 YES to Jack and 10 NO to Jill.
Finally, letâ€™s consider the case where both uses want to sell complementary assets. Jack places an order to sell 20 YES @$0.75. Jill places an order to sell 20 NO @$0.25. A match is again possible in this scenario, this time employing the merge operation. It works as follows, transfer 20 YES from Jack and 20 NO from Jill and merge them via the CTF into $20 and distribute $15 to Jack and $5 to Jill.
As illustrated with the examples and diagram above, bids for token A at price P are equivalent to asks for token Aâ€™ (the complement) at price 1-P and vise versa. Liquidity is thus shared between complementary binary outcome tokens. Polymarketâ€™s audited custom exchange contract (CTFExchange) supports this unified book structure and the matching service calculates matches accordingly.
The goal of Polymarketâ€™s market making reward program is to catalyze a healthy and liquid marketplace. Specifically, this means encouraging passive, balanced quoting that is tight to a marketâ€™s midpoint throughout the duration of the marketâ€™s lifecycle and done so across many unique markets. In order to do this, we have taken dYdXâ€™s successful and ongoing incentive program, which shares a similar goal, and made a few adjustments to better suit our market structure and platform idiosyncrasies. The primary changes include specific consideration for binary market liquidity (ie a bid on A counts as an ask on Aâ€™), the lack of any staking mechanic, a slightly modified order utility-relative to depth function and reward amounts isolated per market. Rewards are distributed using a fork of 1inchâ€™s merkle distributor (itself a fork of Uniswapâ€™s merkle distributor) at the cadence of weekly epochs. Rewards are currently funded in $UMA as provided by the UMA DAO (proposal).
By posting resting limit orders, market makers are automatically eligible for Polymarketâ€™s incentive program. Scoring happens according to the following formulas documented here which factor two-sided quoting with complementary consideration, quote tightness (vs midpoint) and uptime. The amount of rewards earned is determined by the relative share of each participantâ€™s Q_final
in each market multiplied by the rewards available for that market. Sampling of orders happens randomly every minute. We will walk through a complete scoring example below.
Letâ€™s consider two scoring markets with the following configurations.
Market X (Outcome Tokens X and Xâ€™):
min_size
- 100 shares
max_spread
- 5c
reward
- 75
Market Y (Outcome Tokens Y and Yâ€™):
min_size
- 10 shares
max_spread
- 3c
reward
- 100
Letâ€™s assume there are only two market makers with live limit orders when we sample. Their orders are listed below with the equivalent order for the outcomeâ€™s complement in parentheses.
User A:
100 BID X @ .32 (== 100 ASK Xâ€™ @.68)
700 BID X @.31 (== 700 ASK Xâ€™ @.69)
300 BID Xâ€™ @.62 (== 300 ASK X @.38)
1000 BID Xâ€™ @.60 (== 1000 ASK X @.40)
500 BID Y @.71 (== 500 ASK Yâ€™ @.29)
200 BID Y @.70 (== 200 ASK Yâ€™ @.30)
420 BID Y @.69 (== 420 ASK Yâ€™ @.31)
100 BID Yâ€™ @.27 (== 100 ASK Y @.73)
User B:
50 BID X @.34 (== 50 ASK Xâ€™ @.66)
5 BID X @.33 (== 5 ASK Xâ€™ @.67)
100 ASK X @.36 (== 100 BID Xâ€™ @.64)
15 BID Yâ€™ @.27 (== 15 ASK Y @.73)
10 ASK Yâ€™ @.29 (== 10 BID Y @.71)
To score orders at time n
, we begin by filtering orders that donâ€™t meet the configured min_size
scoring requirements. We apply the following filter:
size>=min_size
Only User Bâ€™s order #2 is removed by this filter.
Next, we must calculate the midpoint for the outcome tokens. Based on the orders listed above we determine the midpoint of X to be .35, thus the midpoint of Xâ€™ is 1-.35 = .65. Similarly we calculate the midpoint of Y to be .72 based on the orders listed above and the midpoint of Yâ€™ to be 1-.72 = .28. Now that we know our midpoints and based on the configured max_spreads
we can filter orders to only include those within the following ranges defined by the function:
midpoint-max_spread<price<midpoint+max_spread
Respectively, we calculate the following market ranges (.30, .40), (.60, .70), (.69, .75), (.25, .31). After applying this filter the following orders for User A remain: 1, 2, 3, 5, 6, 8. Then for User B 1, 3, 4, 5 remain.
The next step is to actually score each individual order. We do so by applying the order position scoring function:
((max_spread - order_spread)/max_spread)^2
.
We will work through scoring User Aâ€™s order #1 in full. For this order, the order_spread
is 3 (3c from the midpoint), and the max_spread
is 5 thus it receives a score of (5-3/5)^2 = .16. Applying the same calculation to the rest of the orders we get the following values for User Aâ€™s orders respectively .04, .16, .44, .11, .44 and for User Bâ€™s remaining orders we get the respective values: .64, .64, .44, .44.
After calculating the individual order scores, we must calculate the â€śside scoresâ€ť, weighted by order sizes and considering complements. To do this, we group orders on the same sides considering them within the view of one book. This means for each user there will be two groups per market. For user A the first group of remaining orders for market X is 1 and 2 and the second group is 3. For Market Y user Aâ€™s two groups are 5 and 6 then 8. Repeating the same for user B we get the following groupings. User Bâ€™s group 1 for market X is 1, group 2 is 3 then for market Y group 1 is 4 and group 2 is 5.
Now, for each group we calculate the â€śside scoreâ€ť we apply the function:
sum(order_score*order_size)
So for user A, market X, group 1 we get .16100+.04700=44; for user A market X group 2 we get .16300=48; for user A market Y group 1 we get .44500+.11200=242; and then finally for user A market Y group 2 we get .44100=44. For user B we get the following four values in the same order as user A: 32, 64, 6.6, 4.4.
Next we reduce all the â€śside scoresâ€ť for each user into a single â€śmarket scoreâ€ť per user per market. To do this, we simply take the minimum of the userâ€™s two â€śside scoresâ€ť for the market fromalized as:
min(group_1_score, group_2_score)
User A has â€śmarket scoresâ€ť 44 for market X and 44 for market Y. User B has â€śmarket scoresâ€ť 32 and 4.4.
These â€śmarket scoresâ€ť are accumulated over an entire epoch and then multiplied by the percentage of samples where the user scored; this yields a userâ€™s q_final
for a market.
sum(market_scores_epoch) * uptime
Considering the case above as a one sample epoch, both users have an uptime of 1 for each market. Therefore, the usersâ€™ q_finals
are equal to their â€śmarket scoresâ€ť.
Finally, rewards are distributed based on a userâ€™s relative q_final
for a market and the configured reward, also expressed as:
q_final/sum(q_final_all)*market_reward
For user A, from market X they will receive (44/(44+32))*75 = 43.42 reward tokens. For market Y, user A will receive (44/(44+4.4))*100 = 90.90 reward tokens. For market X and Y respectively user B will receive 31.58 and 9.1 reward tokens respectively.
Full documentation on the Polymarket Market Maker reward program can be found alongside the API docs here. Information on the specific reward configuration can be discovered by making a GET request to the following endpoint specifying the epoch number. https://strapi-matic.poly.market/reward-epoches/{epoch_number}
.