-
Notifications
You must be signed in to change notification settings - Fork 0
/
quantums.go
157 lines (146 loc) · 5.47 KB
/
quantums.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
package lib
import (
"math/big"
)
// BaseToQuoteQuantums converts an amount denoted in base quantums, to an equivalent amount denoted in quote
// quantums. To determine the equivalent amount, an oracle price is used.
//
// - `priceValue * 10^priceExponent` represents the conversion rate from one full coin of base currency
// to one full coin of quote currency.
// - `10^baseCurrencyAtomicResolution` represents the amount of one full coin that a base quantum is equal to.
// - `10^quoteCurrencyAtomicResolution` represents the amount of one full coin that a quote quantum is equal to.
//
// To convert from base to quote quantums, we use the following equation:
//
// quoteQuantums =
// (baseQuantums * 10^baseCurrencyAtomicResolution) *
// (priceValue * 10^priceExponent) /
// (10^quoteCurrencyAtomicResolution)
// =
// baseQuantums * priceValue *
// 10^(priceExponent + baseCurrencyAtomicResolution - quoteCurrencyAtomicResolution) [expression 1]
//
// The result is rounded down.
func BaseToQuoteQuantums(
bigBaseQuantums *big.Int,
baseCurrencyAtomicResolution int32,
priceValue uint64,
priceExponent int32,
) (bigNotional *big.Int) {
return multiplyByPrice(
new(big.Rat).SetInt(bigBaseQuantums),
baseCurrencyAtomicResolution,
priceValue,
priceExponent,
)
}
// QuoteToBaseQuantums converts an amount denoted in quote quantums, to an equivalent amount denoted in base
// quantums. To determine the equivalent amount, an oracle price is used.
//
// - `priceValue * 10^priceExponent` represents the conversion rate from one full coin of base currency
// to one full coin of quote currency.
// - `10^baseCurrencyAtomicResolution` represents the amount of one full coin that a base quantum is equal to.
// - `10^quoteCurrencyAtomicResolution` represents the amount of one full coin that a quote quantum is equal to.
//
// To convert from quote to base quantums, we use the following equation:
//
// baseQuantums =
// quoteQuantums / priceValue /
// 10^(priceExponent + baseCurrencyAtomicResolution - quoteCurrencyAtomicResolution)
//
// The result is rounded down.
func QuoteToBaseQuantums(
bigQuoteQuantums *big.Int,
baseCurrencyAtomicResolution int32,
priceValue uint64,
priceExponent int32,
) (bigNotional *big.Int) {
// Determine the non-exponent part of the equation.
// We perform all calculations using positive rationals for consistent rounding.
isLong := bigQuoteQuantums.Sign() >= 0
ratAbsQuoteQuantums := new(big.Rat).Abs(
new(big.Rat).SetInt(bigQuoteQuantums),
)
ratPrice := new(big.Rat).SetUint64(priceValue)
ratQuoteQuantumsDivPrice := new(big.Rat).Quo(ratAbsQuoteQuantums, ratPrice)
// Determine the absolute value of the return value.
exponent := priceExponent + baseCurrencyAtomicResolution - QuoteCurrencyAtomicResolution
ratBaseQuantums := new(big.Rat).Quo(
ratQuoteQuantumsDivPrice,
RatPow10(exponent),
)
// Round down.
bigBaseQuantums := BigRatRound(ratBaseQuantums, false)
// Flip the sign of the return value if necessary.
if !isLong {
bigBaseQuantums.Neg(bigBaseQuantums)
}
return bigBaseQuantums
}
// multiplyByPrice multiples a value by price, factoring in exponents of base
// and quote currencies.
// Given `value`, returns result of the following:
//
// `value * priceValue * 10^(priceExponent + baseAtomicResolution - quoteAtomicResolution)` [expression 2]
//
// Note that both `BaseToQuoteQuantums` and `FundingRateToIndex` directly wrap around this function.
// - For `BaseToQuoteQuantums`, substituing `value` with `baseQuantums` in expression 2 yields expression 1.
// - For `FundingRateToIndex`, substituing `value` with `fundingRatePpm * time` in expression 2 yields expression 3.
func multiplyByPrice(
value *big.Rat,
baseCurrencyAtomicResolution int32,
priceValue uint64,
priceExponent int32,
) (result *big.Int) {
ratResult := new(big.Rat).SetUint64(priceValue)
ratResult.Mul(
ratResult,
value,
)
ratResult.Mul(
ratResult,
RatPow10(priceExponent+baseCurrencyAtomicResolution-QuoteCurrencyAtomicResolution),
)
return new(big.Int).Quo(
ratResult.Num(),
ratResult.Denom(),
)
}
// FundingRateToIndex converts funding rate (in ppm) to FundingIndex given the oracle price.
//
// To get funding index from funding rate, we know that:
//
// - `fundingPaymentQuoteQuantum = fundingRatePpm / 1_000_000 * time * quoteQuantums`
// - Divide both sides by `baseQuantums`:
// - Left side: `fundingPaymentQuoteQuantums / baseQuantums = fundingIndexDelta / 1_000_000`
// - right side:
// ```
// fundingRate * time * quoteQuantums / baseQuantums = fundingRatePpm / 1_000_000 *
// priceValue * 10^(priceExponent + baseCurrencyAtomicResolution - quoteCurrencyAtomicResolution) [expression 3]
// ```
//
// Hence, further multiplying both sides by 1_000_000, we have:
//
// fundingIndexDelta =
// (fundingRatePpm * time) * priceValue *
// 10^(priceExponent + baseCurrencyAtomicResolution - quoteCurrencyAtomicResolution)
//
// Arguments:
//
// proratedFundingRate: prorated funding rate adjusted by time delta, in parts-per-million
// baseCurrencyAtomicResolution: atomic resolution of the base currency
// priceValue: index price of the perpetual market according to the pricesKeeper
// priceExponent: priceExponent of the market according to the pricesKeeper
func FundingRateToIndex(
proratedFundingRate *big.Rat,
baseCurrencyAtomicResolution int32,
priceValue uint64,
priceExponent int32,
) (fundingIndex *big.Int) {
return multiplyByPrice(
proratedFundingRate,
baseCurrencyAtomicResolution,
priceValue,
priceExponent,
)
}