A recent discussion on the forums grabbed my attention as it seemed a majority of people had a different understanding about the workings of GTON rewards and APR as I did.
The question is, how should we assign rewards (as APR) for each pool in order to adequetly reward the liquidity providers of the variety of pools.
In short, I postulated that the rewards will only influence the liquidity present within each pool, and will not influence the eventual APR of people holding liquidity in that pool. The APR that each pool will status quo to is determined by the risk/reward characteristics of the combined user base holding GTON. In some cases, users are willing to take higher risks for less rewards. In this case, this refers to providing liquidity to a paired tokens pool instead of staking. Risk: impermanent loss; reward; higher APR.
Link to start discussion: Telegram: Contact @graviton_community
For fun, I decided to make a simulation (in python).
I describe the population of GTON holders by the minimum reward required for them to move their liquidity from staking to liquidity pairs.
For example, a user with min_APR_diff = 0.4
will only provide liquidity to LP when the APR difference is at least 40%. The population is for this example is described as (distribution):
The implemented code basically runs a simulation over x days where liquidity providers change pools depending on whether the constraints are met. On each day, only 5% of the GTON holders are evaluated whether they change pools, ordered according to how long ago the GTON holder was evaluated. Simply put, every GTON holder evaluates their position every 20 days.
For simplicity pf implementation, each liquidity provider is assumed to hold the same amount of GTON. Therefore, # members in pool
is a proxy for the amount of liquidity in that pool.
The change through time of members and APR of each pool where the reward allocated for LP is 4 times higher than staking (rewards = np.array([20, 80])
)
The change through time of members and APR of each pool where the reward allocated for LP is 2 times higher than staking (rewards = np.array([33, 67])
)
As expected, the APR of both pools approach the same status quo, independent of the reward allocation for each pool. Differences in APR between pools can be achieved by changing the decision-making characteristics of the user base.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
samples = 1000
max_change_per_day = 0.05
rewards = np.array([40, 60])
members = np.array([samples, 0])
users = np.abs(np.random.normal(0.4, scale=0.1, size=samples))
data = pd.DataFrame(np.array([users, np.full(users.shape, fill_value=999, dtype=np.int32), np.zeros_like(users, dtype=int)], dtype=object).T, columns=['min_APR_diff', 'days_since_change', 'pool'])
APRs = []
members_d = []
for day in np.arange(50):
members_d.append(members.copy())
APR = rewards/np.maximum(members, 1)
APRs.append(APR)
for i, user in data.sort_values('days_since_change')[-int(samples*max_change_per_day):].iterrows():
APR = rewards/np.maximum(members, 1)
if max(APR) > (user.min_APR_diff+1)*APR[user.pool]:
members[user.pool] -= 1
user.pool = np.argmax(APR)
members[user.pool] += 1
user.days_since_change = 0
data.loc[i] = user
fig, ax = plt.subplots(1,2, figsize=(12,6))
axes = ax.ravel()
labels = ['pool 1', 'pool 2']
lines = []
for pool in range(2):
lines.append(axes[0].scatter(np.arange(len(members_d)), np.array(members_d)[:, pool], ))
axes[1].scatter(np.arange(len(APRs)), np.array(APRs)[:,pool])
axes[0].set_title('# members in pool')
axes[1].set_title('Pool APR')
axes[1].set_ylim(0,0.2)
axes[0].set_xlabel('time')
axes[1].set_xlabel('time')
fig.legend(lines, labels)
fig.suptitle(f'REWARD STAKING/LP = {rewards[0]/rewards[1]} MEMBERS STAKING/POOL = {members_d[-1][0]/members_d[-1][1]}')