Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Propose a solution for the funding rate #1233

Merged
merged 3 commits into from
Sep 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 113 additions & 0 deletions docs/007-funding-rate.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
= ADR 007 - Funding rate
Lucas Soriano <[email protected]>
:toc:
:icons: font
:attributes: 2023-09-06

Status: Proposed

== Decision

Use the `ContractInput` argument when calling `dlc_manager::Manager::renew_offer`.
By setting new values of `offer_collateral` and `accept_collateral` on `ContractInput` we can rebalance the margins between the two parties, which will have the desired effect on the payouts.

== Context

The funding rate is the cost of holding a perpetual swap position that is on the side of the market sentiment.
There needs to be a funding rate because that is what motivates traders to keep their perpetual swaps positions open _against_ the market sentiment.
The sign of the funding rate determines which of the two parties involved must pay the funding fee to keep their position open.

In the 10101 ecosystem, the funding rate must come from the 10101 orderbook.
This value will be updated at a fixed interval e.g. every 8 hours.

== Options considered

=== Use the `ContractInput` argument in the `rust-dlc` DLC channel renew protocol

By setting a new collateral distribution between trader and coordinator based on the funding fee, we will recreate the DLC with new margins.
The updated margin will have an effect on how the payout curve should look like.

If the contract is _refunded_ (catastrophic error), the returned collateral will take into account all the funding fees exchanged.

==== Pros

- Atomic update to the DLC and funding fee exchange.
- Easy implementation.
- Funding fee exchange is reflected on the `refund_transaction`.

==== Cons

- Fee is only available after the DLC channel is settled.

=== Lightning payment before the DLC is renewed

The renewal protocol will first require one of the two parties (coordinator and trader) to generate an invoice for the funding fee.
Which party generates the invoice (i.e. the receiver) is determined by the sign of the funding rate of the 10101 orderbook.

Only after the invoice has been paid will the coordinator initiate the renew protocol to roll over the DLC between coordinator and trader.

==== Pros

- The DLC remains unaffected by the exchanged funding rate.
- Can be implemented without changes to our dependencies.
- Easy to track the funding fee payments.
- Fee is readily available to use after.

==== Cons

===== No execution guarantees

There is no guarantee that the renew protocol will succeed after the funding fee payment is claimed.
Depending on the size of the position, it could be economically beneficial for a party to claim the funding fee paid by the other party and then force-close the channel.
As such, in some scenarios we would be trusting the counterparty to not run away with an unearned funding fee.

With PTLCs we might be able to make the DLC channel update and the payment atomic, but that is not yet implemented.

===== Funds required on Lightning channel

To be able to pay the funding fee the payer will need to have sufficient outbound liquidity.
It might happen that a user will need to stop trading if their Lightning channel cannot cover the costs.

They would still be able to close their position and open a new one.

=== Adjust the CET payouts depending on the funding fee

If the funding fee is `X` paid from `A` to `B`, for every `o_A`, CET output amount belonging to `A`, we would subtract `X`: `o_A - X`; and for every `o_B`, CET output belonging to `B`, we would add `X`: `o_B + X`.

Unfortunately this doesn't work in all scenarios:

- If a CET already only pays to `B` then the payout cannot be increased!
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is the case the user (A) was basically liquidated.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is true. But that does not mean that the funding fees were paid.

Basic example:

  • 1st rollover makes A pay to B. The "A gets liquidated" CET cannot get adjusted because the output already pays everything to B.
  • At the updated expiry the oracle implicitly unlocks the "A gets liquidated" CET. B should have earned the full DLC payout plus the funding fee, but they only get the DLC payout.

In a more extreme scenario, if the contract is rolled over 20 times and the funding fee is paid 15 times from A to B, if A gets liquidated with the CET at the boundary of the curve, B misses out on 15 - 5 = 10 trading fees.

Obviously this only needs to apply to contracts that are closed on-chain. If I were keeping track of the funding fees outside of the CETs I would want to do the accounting without capping gains/losses based on the collateral. But this would mean there would be a discrepancy between on-chain and off-chain settlement.

- If a CET has `o_A < X`, then it will not be possible to increase `o_B` sufficiently.

As such, in some scenarios the fee will not be paid.
This problem might compound as the same position might be rolled over multiple times.
If the advantaged party ends up getting liquidated, the winning party will not see any funding fee.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Imho this is a good design: the longer the position is open, the earlier the user gets liquidated because its margin is being reduced by the funding rate.

Expressed differently:

  • Alice has margin m_A
  • Bob has margin m_B
  • Funding rate is f.

After the first rollover, e.g. after 8h, we have

  • Alice margin m_A - f
  • Bob margin m_B + f

The liquidation for the party going long is calculated as:
= (price_opening * leverage)/(leverage+1)

Now, the important thing is that the leverage is only constant at the beginning when the position was opened, later the leverage is a function of the margin and the price, i.e.

  • initially we had: margin = quantity / (price_opening * leverage)
  • after the opening we have leverage = quantity / (price_opening * margin)

Which means, the liquidation price will change but each party will always get the funding rate until either of the party gets liquidated, i.e. does not have enough money to pay for the funding rate.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a couple of reasons why I don't like this idea so much:

  • Makes liquidation more likely, which is the worst case scenario for traders and is expensive and cumbersome to enforce.
  • Traders can miss out on funding fees (see comment above).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes liquidation more likely, which is the worst case scenario for traders and is expensive and cumbersome to enforce.

Yes, but I don't think it's that much more likely, e.g. current funding rate on BitMEX is 0.01% for 8h, meaning, his margin gets reduced by 0.01% and it's 0.01% more likely that you get liquidated. That's like nothing :D


==== Pros

- We already did something similar in ItchySats.
- The funding fee payment and the DLC renewal are atomic.

==== Cons

- Fee cannot be charged in all cases.
- Fee is only available after the DLC channel is settled.

=== Use the dedicated `counter_payout` argument in the `rust-dlc` DLC channel renew protocol

Looking at the `dlc_manager::Manager::renew_offer` API we noticed a `counter_payout` argument which we thought we could use to adjust the payouts between the two parties.
It turns out that this field is only used to choose a kind of symbolic payout for the DLC that is being replaced, so that it can be closed with that value.
In practice this is not really useful in itself as it has no effect on the new DLC{empty}footnote:[We should probably consider removing this unnecessary complexity from `rust-dlc`, particularly because consumers do not care to set this value.].

All in all, this solution simply does not work.

== Consequences

By choosing to pay the funding fee using the Lightning channel we get a simple solution to the problem.
This comes at the cost of atomicity, but the only solution that ensures atomicity does not work in all cases.
Furthermore, atomicity can be achieved once PTLCs are supported by `rust-lightning`.

== Advice

- Philipp on Sep 6, 2023: Adjusting the CET payouts depending on the funding fee is preferred over the Lightning payment before the DLC is renewed.
- Lucas on Sep 7, 2023: The renew protocol _can_ be used to solve this problem, just not in the way that we originally imagined.
1 change: 1 addition & 0 deletions docs/readme.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ This is an overview of all documents related to 10101.
. link:../docs/004-solution-architecture.adoc[ADR-004 Solution Architecture]
. link:../docs/005-mobile-app-routing.adoc[ADR-005 Mobile App Routing]
. link:../docs/006-just-in-time-channels.adoc[ADR-006 Just-in-time channels]
. link:../docs/007-funding-rate.adoc[ADR-007 Funding rate]