TokenEngineeringCommunity / BalancerPools_Model

cadCAD model to simulate Balancer AMMs
MIT License
54 stars 29 forks source link

post_processing: add token_k_value and TVL to p_df #45

Open AngelaKTE opened 3 years ago

AngelaKTE commented 3 years ago
AngelaKTE commented 3 years ago
AngelaKTE commented 3 years ago
markusbkoch commented 3 years ago
  • 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?

image

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. image

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. image

mzargham commented 3 years ago

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.

markusbkoch commented 3 years ago

I really like the last plot in the notebook when you swap total_token_balances for the invariant. It highlights Z's point about <steady growth during long periods without join/exits> vs. <abrupt change upon join/exits> image

AngelaKTE commented 3 years ago
  • 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?

image

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. image

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. image

@markusbkoch I have a question on the correct implementation of the Invariant here.

Which one is correct, and why?

markusbkoch commented 3 years ago

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).

AngelaKTE commented 3 years ago

Update post-processing:

AngelaKTE commented 3 years ago
  • 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?

image

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. image

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. image

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.

mzargham commented 3 years ago

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).

VasilySumanov commented 3 years ago

@mzargham thank you for such a detailed description! For me, the energy conservation analogy perfectly helps in-depth understanding of this term.

AngelaKTE commented 3 years ago

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. :)

Ramarti commented 3 years ago

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?

image

markusbkoch commented 3 years ago

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. image

If you want to go as far in the x axis (ie, after the drop), maybe try log scale in the primary y axis?

Ramarti commented 3 years ago

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. image

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!

AngelaKTE commented 3 years ago
AngelaKTE commented 3 years ago

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?

VasilySumanov commented 3 years ago

@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.

AngelaKTE commented 3 years ago

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