Skip to content

Commit

Permalink
Merge pull request #2001 from vegaprotocol/perps-additions
Browse files Browse the repository at this point in the history
Perps: modify funding payment calculation
  • Loading branch information
Witold authored Nov 6, 2023
2 parents a826f05 + b4024d2 commit 690ad9e
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 40 deletions.
133 changes: 94 additions & 39 deletions protocol/0053-PERP-product_builtin_perpetual_future.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,62 +83,63 @@ Every time a [mark to market settlement](./0003-MTMK-mark_to_market_settlement.m

When the `settlement_schedule` event is received we need to calculate the funding payment. Store the current vega time as `funding_period_end`.

If there are no oracle data points with a timestamp less than `funding_period_end` available then funding payment is skipped and `funding_period_start` gets overwritten with `funding_period_end`.
Skip the funding payment calculation (set payment to `0`) if any of the following conditions is met:

If such points are available then the calculations discussed in the following subsections get executed and funding payments get exchanged.
- no spot (external) data has been ingested since market was created,
- market has been in auction throughout the entire funding period.

#### TWAP spot price calculation
Please refer to the following subsections for the details of calculation of the funding payment if none of the above conditions are met.

Traverse all the available oracle data point tuples `(s,t)` and calculate the time-weighted average spot price (`s_twap`) as:
#### TWAP calculation

Same methodology applies to spot (external) and perps (internal). The available prices (spot and perps considered separately of course) should be used to calculate the time weighted average price within the funding period. If no observations at or prior to the funding period start exist then the start of the period used for calculation should be moved forward to the time of the first observation. An observation is assumed to be applying until a subsequent observation is available. Periods spent in auction should be excluded from the calculation. This implies that spot datapoints received during the auction except for the latest one should be disregarded. Please refer to the acceptance criteria for a detailed example.

Calculation of the TWAP is carried out by maintaining the following variables: `numerator`, `denominator`, `previous_price` and `previous_time`. It's also assumed that a function `current_time()` is available which returns the current Vega time, and a function `in_auction()` exists which returns `true` if the market being considered is currently in auction and `false` otherwise.
The variables are maintained as follows.

When a new `price` observation arrives:

```go
var previous_point
sum_product := 0
for p := range oracle_data_points {
if p.t <= funding_period_start {
previous_point = p
continue
}
if p.t >= funding_period_end {
break
}
if previous_point != nil {
sum_product += previous_point*(p.t-max(funding_period_start,previous_point.t))
}
previous_point = p
if previous_price != nil && in_auction() {
time_delta = current_time()-previous_time
numerator += previous_price*time_delta
denominator += time_delta
}
previous_price = price
previous_time = current_time()
```

When the market goes into auction:

sum_product += previous_point.s*(funding_period_end-max(funding_period_start,previous_point.t))
s_twap = sum_product / (funding_period_end - max(funding_period_start, oracle_data_points[0].t))
```go
time_delta = current_time()-previous_time
numerator += previous_price*time_delta
denominator += time_delta
```

Only the oracle data point with largest timestamp that's less than or equal to `funding_period_end` (and any data points with larger timestamps) need to be kept from that point on.
When the market goes out of auction:

#### TWAP mark price calculation
```go
previous_time = current_time()
```

Traverse all the available internal data point tuples `(f,t)` and calculated the time-weighted average mark price (`f_twap`) as:
When the funding payment cue arrives TWAP gets calculated and returned as:

```go
var previous_point
sum_product := 0
for p := range internal_data_points {
if p.t <= funding_period_start {
previous_point = p
continue
}
if previous_point != nil {
sum_product += previous_point*(p.t-max(funding_period_start,previous_point.t))
}
previous_point = p
if !in_auction() {
time_delta = current_time()-previous_time
numerator += previous_price*time_delta
denominator += time_delta
}
previous_time = current_time
if denominator == 0 {
return 0
}
return numerator / denominator
sum_product += previous_point.f*(funding_period_end-max(funding_period_start,previous_point.t))
f_twap = sum_product / (funding_period_end - max(funding_period_start, internal_data_points[0].t))
```

Only the internal data point with largest timestamp needs to be kept from that point on.
Note that depending on what type of oracle is used for the spot price it may be that the oracle points only become known shortly before or at the funding payment cue time, so the above pseudocode is just an illustration of how these quantities should be calculated and the implementation will need to be able to apply such calculation retrospectively.

#### Funding payment calculation

Expand Down Expand Up @@ -234,3 +235,57 @@ In both cases the estimates are for a hypothetical position of size 1.
1. A perpetual market which is active and has open orders, after checkpoint restart, is in opening auction. All margin accounts are transferred to general accounts. (<a name="0053-PERP-022" href="#0053-PERP-022">0053-PERP-022</a>)
1. A perpetual market which is active and has open orders. Wait for a new network history snapshot to be created. Load a new data node from network history. All market data is preserved. (<a name="0053-PERP-023" href="#0053-PERP-023">0053-PERP-023</a>)
1. When the funding payment does not coincide with mark to market settlement time, a party has insufficient funds to fully cover their funding payment such that the shortfall amount if $x$ and the balance of market's insurance pool is $\frac{x}{3}$, then the entire insurance pool balance gets used to cover the shortfall and the remaining missing amount $\frac{2x}{3}$ gets dealt with using loss socialisation. (<a name="0053-PERP-024" href="#0053-PERP-024">0053-PERP-024</a>)

1. Assume a market trades steadily generating a stream in mark price observations, but the first spot price observation only arrives during the 4th funding period of that market. Then funding payments for periods 1, 2 and 3 all equal 0. (<a name="0053-PERP-025" href="#0053-PERP-025">0053-PERP-025</a>)

1. Assume the market has been in a long auction so that a funding period has started and ended while the market never went back into continuous trading. In that case the funding payment should be equal to 0 and no transfers should be exchanged. (<a name="0053-PERP-026" href="#0053-PERP-026">0053-PERP-026</a>)

1. Assume a 10 minute funding period. Assume a few funding periods have already passed for this market.

Assume the last known mark price before the start of the period to be `10` and that it gets updated every 2 minutes as follows:
| Time (min) since period start | mark price |
| ----------------------------- | ----------- |
| 1 | 11 |
| 3 | 10 |
| 5 | 9 |
| 7 | 8 |
| 9 | 7 |

Assume the last known spot price before this funding period is `11`. Then assume the subsequent spot price observations get ingested according to the schedule specified below:
| Time (min) since period start | spot price |
| ----------------------------- | ----------- |
| 1 | 9 |
| 3 | 10 |
| 5 | 12 |
| 6 | 11 |
| 7 | 8 |
| 9 | 14 |

Then, assuming no auctions during the period we get:
$\text{internal TWAP}= \frac{10\cdot(1-0)+11\cdot(3-1)+10\cdot(5-3)+9\cdot(7-5)+8\cdot(9-7)+7\cdot(10-9)}{10}=9.3$,
$\text{external TWAP}=\frac{11\cdot(1-0)+9\cdot(3-1)+10\cdot(5-3)+12\cdot(6-5)+11\cdot(7-6)+8\cdot(9-7)+14\cdot(10-9)}{10}=10.3$. (<a name="0053-PERP-027" href="#0053-PERP-027">0053-PERP-027</a>)

1. Assume a 10 minute funding period. Assume a few funding periods have already passed for this market. Furthermore, assume that in this period that market is in an auction which starts 5 minutes into the period and ends 7 minutes into the period.

Assume the last known mark price before the start of the period to be `10` and that it gets updated as follows:
| Time (min) since period start | mark price |
| ----------------------------- | ----------- |
| 1 | 11 |
| 3 | 10 |
| 7 | 9 |
| 8 | 8 |
| 10 | 30 |

Assume the last known spot price before this funding period is `11`. Then assume the subsequent spot price observations get ingested according to the schedule specified below:
| Time (min) since period start | spot price |
| ----------------------------- | ----------- |
| 1 | 9 |
| 3 | 10 |
| 5 | 12 |
| 6 | 11 |
| 8 | 8 |
| 9 | 14 |

Then, taking the auction into account we get:
$\text{internal TWAP}= \frac{10\cdot(1-0)+11\cdot(3-1)+10\cdot(5-3)+9\cdot(8-7)+8\cdot(10-8)+30\cdot(10-10)}{8}=9.625$,
$\text{external TWAP}=\frac{11\cdot(1-0)+9\cdot(3-1)+10\cdot(5-3)+11\cdot(8-7)+8\cdot(9-8)+14\cdot(10-9)}{10}=10.25$. (<a name="0053-PERP-028" href="#0053-PERP-028">0053-PERP-028</a>)
10 changes: 9 additions & 1 deletion protocol/features.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,15 @@
"0073-LIMN-110",
"0073-LIMN-111",
"0081-SUCM-015",
"0053-PERP-024"
"0014-ORDT-120",
"0014-ORDT-121",
"0014-ORDT-122",
"0014-ORDT-123",
"0053-PERP-024",
"0053-PERP-025",
"0053-PERP-026",
"0053-PERP-027",
"0053-PERP-028"
]
},
"Team rewards": {
Expand Down
3 changes: 3 additions & 0 deletions wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ DAI
dApp
datanode
datapoint
datapoints
datatypes
datetime
decentralised
Expand Down Expand Up @@ -200,6 +201,8 @@ PDP
performant
permissioned
permissionless
PERP
perps
PME
PnL
PoS
Expand Down

0 comments on commit 690ad9e

Please sign in to comment.