post_processing: add token_k_value and TVL to p_df
AngelaKTE opened this issue ยท 19 comments
- various plots need token_k_value/TVL over time
- add column providing token_k_value = token_k_balance * token_k_price
- add column providing TVL = sum of all token_k_value
- add column "total_token_balances" = sum of all token_k_balances (=Pool Size)
- calculate "token_a_balance" + "token_b_balance" + "token_c_balance" + ...
- all columns added to one big dataframe "p_df"
- add column "total_token_balances" = sum of all token_k_balances (=Pool Size)
If I understand correctly, if you're looking for a metric of the "pool size" may I suggest the invariant instead?
The sum of token balances can be misleading. Even in the 80/20 ETH/DAI pool (0x8b6e6E), it is dominated by the DAI balance. That's why TVL and total_token_balance have a ~perfect correlation: total_token_balance ~= token_dai_balance = 20% * TVL.
See how using the invariant makes things more clear. Despite the TVL growing from under 60M to over 120M, the invariant only grew from ~184k to ~188k. Meaning the growth in TVL was dominated by the price increase of the underlying tokens (namely ETH in this case), with growth of the pool itself playing a lesser role.
I agree with @markusbkoch. An additional note, the invariant view also makes it relatively easy to see the impact of add/remove liquidity events as different from the the steady growth in the "invariant"
side note, we use the term "invariant" as the price regulation in the bonding surface is derived from holding this function V invariant for swaps but in practice, the fees cause the value of V to increase for swaps. This is how the LPs make money over time. On the other hand the add and remove liquidity mechanisms explicitly increase and decrease V, while preserving price. Furthermore, the awesome balancer feature "asymmetric" liquidity add/remove is actually a composition of a swap and a liquidity add.
- add column "total_token_balances" = sum of all token_k_balances (=Pool Size)
If I understand correctly, if you're looking for a metric of the "pool size" may I suggest the invariant instead?
The sum of token balances can be misleading. Even in the 80/20 ETH/DAI pool (0x8b6e6E), it is dominated by the DAI balance. That's why TVL and total_token_balance have a ~perfect correlation: total_token_balance ~= token_dai_balance = 20% * TVL.
See how using the invariant makes things more clear. Despite the TVL growing from under 60M to over 120M, the invariant only grew from ~184k to ~188k. Meaning the growth in TVL was dominated by the price increase of the underlying tokens (namely ETH in this case), with growth of the pool itself playing a lesser role.
@markusbkoch
I have a question on the correct implementation of the Invariant here.
- Invariant is the product of all (token_(symbol)balance **token(symbol)_weight)
- however, we frequently find two versions of the weight value, and I'm uncertain which one to use (and implement) in the model
a) the weight in percent: 80 WETH / 20 DAI in our case
b) the weight as decimal: 0.8 WETH/ 0.2 DAI (which you obviously use for the plots)
Which one is correct, and why?
The white paper defines it in terms of the normalized weights (such that the sum of all normalized weights is 1) - so 0.8 and 0.2 in that case.
As for the why, it's not explicitly stated in the white paper, but from eq 10 in this proof we see that Wn must be a number between 0 and 1 (since 0<Vn<V by definition).
Update post-processing:
-
remove "total_token_balances" column and post-processing
-
add column "pool_size_V" = product of all (token_(symbol)balance **token(symbol)_weight) - see above
-
"pool_size_V" = product of all (token_(symbol)balance **token(symbol)_weight) - see above
- add column "total_token_balances" = sum of all token_k_balances (=Pool Size)
If I understand correctly, if you're looking for a metric of the "pool size" may I suggest the invariant instead?
The sum of token balances can be misleading. Even in the 80/20 ETH/DAI pool (0x8b6e6E), it is dominated by the DAI balance. That's why TVL and total_token_balance have a ~perfect correlation: total_token_balance ~= token_dai_balance = 20% * TVL.
See how using the invariant makes things more clear. Despite the TVL growing from under 60M to over 120M, the invariant only grew from ~184k to ~188k. Meaning the growth in TVL was dominated by the price increase of the underlying tokens (namely ETH in this case), with growth of the pool itself playing a lesser role.
What do you think about stating it as metric "Pool Size V"? @markusbkoch
Sure it's the Invariant, but since it changes due to most state updates, this term causes confusion.
Sure it's the Invariant, but since it changes due to most state updates, this term causes confusion.
I agreed the term "invariant" causes confusion. I think this is a good opportunity to clarify it though. The practical reason why the function is called "invariant" is because the basic mechanism for swapping one token for another is derived from asserting
In my paper on using state space models to derive economic properties, i called
The functions
When a liquidity provider adds liquidity they are explicitly "charging up" increasing
I hope this helps improve your understanding of both where the term invariant came from (derivation of the swap mechanism), and why we still use it (the energy conservation analogy).
@mzargham thank you for such a detailed description!
For me, the energy conservation analogy perfectly helps in-depth understanding of this term.
Aha! This is a very good mental model (energy)! Given that, this irritation with the Invariant is most welcome.
Initially, we were looking for a better metric to express "liquidity" or "market cap", since these metrics are not well defined (sometimes refering to $USD value, sometimes a more general term for "size"),
and thus are not helpful to demonstrate the interconnectedness of balance, weights, and ratio.
The Invariant certainly is.
After reading your paper, I'd like to rethink the first part of "Understanding Balancer Pools" (the article) - to not only offer another metric but to put more emphasis on "provable system properties" and the engineering perspective.
Which is the whole purpose of this project. :)
Hi @markusbkoch and @AngelaKTE
I added invariant calculation like this:
# Calculate Invariant column
df['invariant'] = 1
for s in symbols:
df['invariant'] *= (df[f'token_{s}_balance'] ** df[f'token_{s}_weight'])
The results i get are...invariant. Am I missing something?
The results i get are...invariant. Am I missing something?
It's just that the range of the primary y axis is too wide. See the plot in my comment above (but notice the invariant in that case is in the secondary y-axis). Before the recent massive drop in liquidity, the invariant was within the 180k-190k range.
If you want to go as far in the x axis (ie, after the drop), maybe try log scale in the primary y axis?
The results i get are...invariant. Am I missing something?
It's just that the range of the primary y axis is too wide. See the plot in my comment above (but notice the invariant in that case is in the secondary y-axis). Before the recent massive drop in liquidity, the invariant was within the 180k-190k range.
If you want to go as far in the x axis (ie, after the drop), maybe try log scale in the primary y axis?
Right, fixed thank you!
- invariant implemented to V1.0 simulation output (post-processing)
- total_token_balances removed from simulation output
Sure it's the Invariant, but since it changes due to most state updates, this term causes confusion.
I agreed the term "invariant" causes confusion. I think this is a good opportunity to clarify it though. The practical reason why the function is called "invariant" is because the basic mechanism for swapping one token for another is derived from asserting
$V(X^+) = V(X)$ where$X$ is the state space of the pool. However, the inclusion of the fees changes in a careful way changes this to a directionally controlled 'differential variant'$V(X^+)> V(X)$ for all swaps. This is how the LPs make money. So to your point it is not "invariant" its actually a "variant". (further reading on invariants and variant here (https://www.cs.cmu.edu/~aplatzer/logic/diffinv.html#differential-invariant).In my paper on using state space models to derive economic properties, i called
$V(X)$ the 'value function' and likened it to a Lyapunov function. Lyapunov functions are advanced control theory math tools for deriving and proving equilibria. Furthermore, their standard form is continuous time, but it also has a version in discrete time.The functions
$V(X)$ we are using are "energy conservation" functions, which is part of the reason the term "invariant" is still a meaningful intuition. It is not that the energy cannot go up, actually it does go up, but that energy is captured from somewhere. It is absolutely critical that the$V(X)$ function never go up, UNLESS the energy it captures comes from somewhere, in this case it comes from applying the fee (which we can think of as a friction) on the swap mechanism. The frictionless swap preserves$V(X)$ but the swap with friction is a bit like charging up a ballon by rubbing it on a carpet. We've not violated the conservation of energy invariant, we've simply collected energy from outside.When a liquidity provider adds liquidity they are explicitly "charging up" increasing
$V(X)$ for a right to discharge it in the future, but thanks to ongoing collection of energy through the friction of fees, it becomes a viable long term investment, despite the fact that there may be impermanent loss due to the fluctuation in the relative prices of the tokens within the pool.I hope this helps improve your understanding of both where the term invariant came from (derivation of the swap mechanism), and why we still use it (the energy conservation analogy).
@mzargham I wonder how weights changes fit in here, since it violates energy conservation? Depending on the current value distribution, a change of weights (without adapting balances) will either destroy energy (black hole?) - or more energy is available, like in nuclear fission.
Either way since it changes the "energy" distribution, it's a major disruption. How would you put it into words?
@AngelaKTE I think dynamic weights changing in the live pool does not violate energy conservation but re-distributes energy between pool components. Let's imagine AMM that has an external controller that changes weights trying to minimize arbitrage gap -> 0 (tracking prices on external markets "ideally" and de-facto changing weights according to these changes).
So, basically invariant wouldn't be changed, but weights of components will.
I've created a tiny series of weights and the impact on Invariant V, please take a look at how Invariant V changes.
https://github.com/TokenEngineeringCommunity/BalancerPools_Model/blob/NBupdate/WeightsChanges.ipynb
However, I get your point, I think it's just not redistributing energy within the pool, rather charging up/off according to external signals.
I've added all our fresh insights to this article, feel free to comment @everyone:
https://docs.google.com/document/d/1M5EpOhmO91nI00LCvzyNBa4I1EKm-0ztq8g_-r6W7wM/edit?usp=sharing