@reserve-protocol/dtf-rebalance-lib 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/open-auction.d.ts +3 -3
- package/dist/open-auction.js +67 -60
- package/dist/start-rebalance.d.ts +2 -2
- package/dist/start-rebalance.js +11 -9
- package/package.json +1 -1
package/dist/open-auction.d.ts
CHANGED
|
@@ -58,12 +58,12 @@ export declare const getTargetBasket: (_initialWeights: WeightRange[], _prices:
|
|
|
58
58
|
*
|
|
59
59
|
* @param rebalance The result of calling folio.getRebalance(), today
|
|
60
60
|
* @param _supply {share} The totalSupply() of the basket, today
|
|
61
|
-
* @param
|
|
61
|
+
* @param _initialAssets {tok} Initial asset balances in the Folio, e.g result of folio.totalAssets() at time rebalance was first proposed
|
|
62
62
|
* @param _targetBasket D18{1} Result of calling `getTargetBasket()`
|
|
63
|
-
* @param
|
|
63
|
+
* @param _assets {tok} Current asset balances in the Folio, e.g result of folio.totalAssets(), today
|
|
64
64
|
* @param _decimals Decimals of each token
|
|
65
65
|
* @param _prices {USD/wholeTok} USD prices for each *whole* token, today
|
|
66
66
|
* @param _priceError {1} Price error to use for each token during auction pricing; should be smaller than price error during startRebalance
|
|
67
67
|
* @param _finalStageAt {1} The % rebalanced from the initial Folio to determine when is the final stage of the rebalance
|
|
68
68
|
*/
|
|
69
|
-
export declare const getOpenAuction: (rebalance: Rebalance, _supply: bigint,
|
|
69
|
+
export declare const getOpenAuction: (rebalance: Rebalance, _supply: bigint, _initialAssets: bigint[] | undefined, _targetBasket: bigint[] | undefined, _assets: bigint[], _decimals: bigint[], _prices: number[], _priceError: number[], _finalStageAt: number, debug?: boolean) => [OpenAuctionArgs, AuctionMetrics];
|
package/dist/open-auction.js
CHANGED
|
@@ -53,24 +53,24 @@ exports.getTargetBasket = getTargetBasket;
|
|
|
53
53
|
*
|
|
54
54
|
* @param rebalance The result of calling folio.getRebalance(), today
|
|
55
55
|
* @param _supply {share} The totalSupply() of the basket, today
|
|
56
|
-
* @param
|
|
56
|
+
* @param _initialAssets {tok} Initial asset balances in the Folio, e.g result of folio.totalAssets() at time rebalance was first proposed
|
|
57
57
|
* @param _targetBasket D18{1} Result of calling `getTargetBasket()`
|
|
58
|
-
* @param
|
|
58
|
+
* @param _assets {tok} Current asset balances in the Folio, e.g result of folio.totalAssets(), today
|
|
59
59
|
* @param _decimals Decimals of each token
|
|
60
60
|
* @param _prices {USD/wholeTok} USD prices for each *whole* token, today
|
|
61
61
|
* @param _priceError {1} Price error to use for each token during auction pricing; should be smaller than price error during startRebalance
|
|
62
62
|
* @param _finalStageAt {1} The % rebalanced from the initial Folio to determine when is the final stage of the rebalance
|
|
63
63
|
*/
|
|
64
|
-
const getOpenAuction = (rebalance, _supply,
|
|
64
|
+
const getOpenAuction = (rebalance, _supply, _initialAssets = [], _targetBasket = [], _assets, _decimals, _prices, _priceError, _finalStageAt, debug) => {
|
|
65
65
|
if (debug === undefined) {
|
|
66
66
|
debug = true;
|
|
67
67
|
}
|
|
68
68
|
if (debug) {
|
|
69
|
-
console.log("getOpenAuction", rebalance, _supply,
|
|
69
|
+
console.log("getOpenAuction", rebalance, _supply, _initialAssets, _targetBasket, _assets, _decimals, _prices, _priceError, _finalStageAt);
|
|
70
70
|
}
|
|
71
71
|
if (rebalance.tokens.length != _targetBasket.length ||
|
|
72
|
-
_targetBasket.length !=
|
|
73
|
-
|
|
72
|
+
_targetBasket.length != _assets.length ||
|
|
73
|
+
_assets.length != _decimals.length ||
|
|
74
74
|
_decimals.length != _prices.length ||
|
|
75
75
|
_prices.length != _priceError.length) {
|
|
76
76
|
throw new Error("length mismatch");
|
|
@@ -94,10 +94,10 @@ const getOpenAuction = (rebalance, _supply, _initialFolio = [], _targetBasket =
|
|
|
94
94
|
const priceError = _priceError.map((a) => new utils_1.Decimal(a.toString()));
|
|
95
95
|
// {tok/wholeTok}
|
|
96
96
|
const decimalScale = _decimals.map((a) => new utils_1.Decimal(`1e${a}`));
|
|
97
|
-
// {wholeTok
|
|
98
|
-
const
|
|
99
|
-
// {wholeTok
|
|
100
|
-
const
|
|
97
|
+
// {wholeTok} = {tok} / {tok/wholeTok}
|
|
98
|
+
const initialAssets = _initialAssets.map((bal, i) => new utils_1.Decimal(bal.toString()).div(decimalScale[i]));
|
|
99
|
+
// {wholeTok} = {tok} / {tok/wholeTok}
|
|
100
|
+
const assets = _assets.map((bal, i) => new utils_1.Decimal(bal.toString()).div(decimalScale[i]));
|
|
101
101
|
// {wholeTok/wholeBU} = D27{tok/BU} * {BU/wholeBU} / {tok/wholeTok} / D27
|
|
102
102
|
let weightRanges = rebalance.weights.map((range, i) => {
|
|
103
103
|
return {
|
|
@@ -108,33 +108,36 @@ const getOpenAuction = (rebalance, _supply, _initialFolio = [], _targetBasket =
|
|
|
108
108
|
});
|
|
109
109
|
const finalStageAt = new utils_1.Decimal(_finalStageAt.toString());
|
|
110
110
|
// ================================================================
|
|
111
|
-
// calculate ideal spot limit, the actual
|
|
112
|
-
// {USD
|
|
113
|
-
const
|
|
111
|
+
// calculate ideal spot limit, the actual basket<->share ratio
|
|
112
|
+
// {USD}
|
|
113
|
+
const folioValue = assets
|
|
114
114
|
.map((f, i) => {
|
|
115
115
|
if (!rebalance.inRebalance[i]) {
|
|
116
116
|
return numbers_1.ZERO;
|
|
117
117
|
}
|
|
118
|
+
// {USD} = {wholeTok} * {USD/wholeTok}
|
|
118
119
|
return f.mul(prices[i]);
|
|
119
120
|
})
|
|
120
121
|
.reduce((a, b) => a.add(b));
|
|
121
|
-
// {USD
|
|
122
|
-
const
|
|
122
|
+
// {USD}
|
|
123
|
+
const basketValue = weightRanges
|
|
123
124
|
.map((weightRange, i) => {
|
|
124
125
|
if (!rebalance.inRebalance[i]) {
|
|
125
126
|
return numbers_1.ZERO;
|
|
126
127
|
}
|
|
127
|
-
|
|
128
|
+
// {USD} = {wholeTok/wholeBU} * {USD/wholeTok} * {wholeBU}
|
|
129
|
+
// assume basket and share are the same
|
|
130
|
+
return weightRange.spot.mul(prices[i]).mul(supply);
|
|
128
131
|
})
|
|
129
132
|
.reduce((a, b) => a.add(b));
|
|
130
|
-
const
|
|
131
|
-
console.log(` 🧺 ${
|
|
133
|
+
const basketPriceChange = basketValue.sub(folioValue).div(folioValue);
|
|
134
|
+
console.log(` 🧺 ${basketPriceChange.mul(100).toFixed(2)}% basket price difference`);
|
|
132
135
|
if (debug) {
|
|
133
|
-
console.log("
|
|
134
|
-
console.log("
|
|
136
|
+
console.log("folioValue", folioValue.toString());
|
|
137
|
+
console.log("basketValue", basketValue.toString());
|
|
135
138
|
}
|
|
136
|
-
if (
|
|
137
|
-
throw new Error("
|
|
139
|
+
if (basketValue.div(folioValue).gt(10) || folioValue.div(basketValue).gt(10)) {
|
|
140
|
+
throw new Error("basketValue and folioValue are too different, something probably went wrong");
|
|
138
141
|
}
|
|
139
142
|
// ================================================================
|
|
140
143
|
// calculate portionBeingEjected
|
|
@@ -144,50 +147,50 @@ const getOpenAuction = (rebalance, _supply, _initialFolio = [], _targetBasket =
|
|
|
144
147
|
ejectionIndices.push(i);
|
|
145
148
|
}
|
|
146
149
|
}
|
|
147
|
-
// {1}
|
|
150
|
+
// {1}
|
|
148
151
|
const portionBeingEjected = ejectionIndices
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
})
|
|
152
|
+
// {USD} = {wholeTok} * {USD/wholeTok}
|
|
153
|
+
.map((i) => assets[i].mul(prices[i]))
|
|
152
154
|
.reduce((a, b) => a.add(b), numbers_1.ZERO)
|
|
153
|
-
.div(
|
|
155
|
+
.div(folioValue);
|
|
154
156
|
// ================================================================
|
|
155
157
|
// calculate progressions
|
|
156
|
-
// {
|
|
157
|
-
const
|
|
158
|
+
// {1} = {USD} / {USD}
|
|
159
|
+
const idealSpotLimit = folioValue.div(basketValue);
|
|
158
160
|
// {wholeBU/wholeShare} = D18{BU/share} / D18
|
|
159
161
|
const prevSpotLimit = new utils_1.Decimal(rebalance.limits.spot.toString()).div(numbers_1.D18d);
|
|
160
|
-
const maxSpotLimit =
|
|
161
|
-
//
|
|
162
|
-
|
|
162
|
+
const maxSpotLimit = idealSpotLimit.gt(prevSpotLimit) ? idealSpotLimit : prevSpotLimit;
|
|
163
|
+
// known: units a little weird here, combining {1} and {wholeBU/wholeShare}
|
|
164
|
+
// {wholeTok} = {wholeTok/wholeBU} * {wholeBU/wholeShare} * {wholeShare}
|
|
165
|
+
const expectedBalances = weightRanges.map((weightRange) => weightRange.spot.mul(maxSpotLimit).mul(supply));
|
|
163
166
|
// absolute scale
|
|
164
|
-
// {1}
|
|
165
|
-
let progression =
|
|
167
|
+
// {1}
|
|
168
|
+
let progression = assets
|
|
166
169
|
.map((actualBalance, i) => {
|
|
167
170
|
if (!rebalance.inRebalance[i]) {
|
|
168
171
|
return numbers_1.ZERO;
|
|
169
172
|
}
|
|
170
|
-
// {wholeTok
|
|
173
|
+
// {wholeTok}
|
|
171
174
|
const balanceInBasket = expectedBalances[i].gt(actualBalance) ? actualBalance : expectedBalances[i];
|
|
172
|
-
// {USD
|
|
175
|
+
// {USD} = {wholeTok} * {USD/wholeTok}
|
|
173
176
|
return balanceInBasket.mul(prices[i]);
|
|
174
177
|
})
|
|
175
178
|
.reduce((a, b) => a.add(b))
|
|
176
|
-
.div(
|
|
179
|
+
.div(folioValue);
|
|
177
180
|
// absolute scale
|
|
178
|
-
// {1}
|
|
179
|
-
const initialProgression =
|
|
181
|
+
// {1}
|
|
182
|
+
const initialProgression = initialAssets
|
|
180
183
|
.map((initialBalance, i) => {
|
|
181
184
|
if (!rebalance.inRebalance[i]) {
|
|
182
185
|
return numbers_1.ZERO;
|
|
183
186
|
}
|
|
184
|
-
// {wholeTok
|
|
187
|
+
// {wholeTok}
|
|
185
188
|
const balanceInBasket = expectedBalances[i].gt(initialBalance) ? initialBalance : expectedBalances[i];
|
|
186
|
-
// {USD
|
|
189
|
+
// {USD} = {wholeTok} * {USD/wholeTok}
|
|
187
190
|
return balanceInBasket.mul(prices[i]);
|
|
188
191
|
})
|
|
189
192
|
.reduce((a, b) => a.add(b))
|
|
190
|
-
.div(
|
|
193
|
+
.div(folioValue);
|
|
191
194
|
if (progression < initialProgression) {
|
|
192
195
|
if (debug) {
|
|
193
196
|
console.log("progression < initialProgression", progression.toString(), initialProgression.toString());
|
|
@@ -246,11 +249,12 @@ const getOpenAuction = (rebalance, _supply, _initialFolio = [], _targetBasket =
|
|
|
246
249
|
const delta = numbers_1.ONE.sub(rebalanceTarget);
|
|
247
250
|
// ================================================================
|
|
248
251
|
// get new limits, constrained by extremes
|
|
249
|
-
// D18{BU/share}
|
|
252
|
+
// D18{BU/share}
|
|
250
253
|
const newLimits = {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
+
// D18{BU/share} = {wholeBU/wholeShare} * {1} * D18
|
|
255
|
+
low: (0, numbers_1.bn)(idealSpotLimit.sub(idealSpotLimit.mul(delta)).mul(numbers_1.D18d)),
|
|
256
|
+
spot: (0, numbers_1.bn)(idealSpotLimit.mul(numbers_1.D18d)),
|
|
257
|
+
high: (0, numbers_1.bn)(idealSpotLimit.add(idealSpotLimit.mul(delta)).mul(numbers_1.D18d)),
|
|
254
258
|
};
|
|
255
259
|
// hold some surpluses aside if ejecting
|
|
256
260
|
if (round == AuctionRound.EJECT) {
|
|
@@ -282,18 +286,20 @@ const getOpenAuction = (rebalance, _supply, _initialFolio = [], _targetBasket =
|
|
|
282
286
|
}
|
|
283
287
|
// ================================================================
|
|
284
288
|
// get new weights, constrained by extremes
|
|
285
|
-
// {wholeBU/wholeShare}
|
|
289
|
+
// {wholeBU/wholeShare}
|
|
286
290
|
const actualLimits = {
|
|
291
|
+
// {wholeBU/wholeShare} = D18{BU/share} / D18
|
|
287
292
|
low: new utils_1.Decimal(newLimits.low.toString()).div(numbers_1.D18d),
|
|
288
293
|
spot: new utils_1.Decimal(newLimits.spot.toString()).div(numbers_1.D18d),
|
|
289
294
|
high: new utils_1.Decimal(newLimits.high.toString()).div(numbers_1.D18d),
|
|
290
295
|
};
|
|
291
296
|
// D27{tok/BU}
|
|
292
297
|
const newWeights = rebalance.weights.map((weightRange, i) => {
|
|
293
|
-
// {wholeTok/wholeBU} = {USD/wholeShare} * {1} / {wholeBU/wholeShare} / {USD/wholeTok}
|
|
294
|
-
const idealWeight =
|
|
295
|
-
// D27{tok/BU}
|
|
298
|
+
// {wholeTok/wholeBU} = {USD} / {wholeShare} * {1} / {wholeBU/wholeShare} / {USD/wholeTok}
|
|
299
|
+
const idealWeight = folioValue.div(supply).mul(targetBasket[i]).div(actualLimits.spot).div(prices[i]);
|
|
300
|
+
// D27{tok/BU}
|
|
296
301
|
const newWeightsD27 = {
|
|
302
|
+
// D27{tok/BU} = {wholeTok/wholeBU} * D27 * {tok/wholeTok} / {BU/wholeBU}
|
|
297
303
|
low: (0, numbers_1.bn)(idealWeight
|
|
298
304
|
.mul(numbers_1.ONE.sub(delta).div(actualLimits.low.div(actualLimits.spot))) // add remaining delta into weight
|
|
299
305
|
.mul(numbers_1.D9d)
|
|
@@ -343,8 +349,9 @@ const getOpenAuction = (rebalance, _supply, _initialFolio = [], _targetBasket =
|
|
|
343
349
|
if (rebalance.priceControl == types_1.PriceControl.NONE) {
|
|
344
350
|
return initialPrice;
|
|
345
351
|
}
|
|
346
|
-
// D27{nanoUSD/tok}
|
|
352
|
+
// D27{nanoUSD/tok}
|
|
347
353
|
const pricesD27 = {
|
|
354
|
+
// D27{nanoUSD/tok} = {USD/wholeTok} * {nanoUSD/USD} / {tok/wholeTok} * D27
|
|
348
355
|
low: (0, numbers_1.bn)(prices[i].mul(numbers_1.ONE.sub(priceError[i])).mul(numbers_1.D9d).div(decimalScale[i]).mul(numbers_1.D27d)),
|
|
349
356
|
high: (0, numbers_1.bn)(prices[i].div(numbers_1.ONE.sub(priceError[i])).mul(numbers_1.D9d).div(decimalScale[i]).mul(numbers_1.D27d)),
|
|
350
357
|
};
|
|
@@ -397,21 +404,21 @@ const getOpenAuction = (rebalance, _supply, _initialFolio = [], _targetBasket =
|
|
|
397
404
|
auctionTokens.push(token);
|
|
398
405
|
auctionWeights.push(newWeights[i]);
|
|
399
406
|
auctionPrices.push(newPrices[i]);
|
|
400
|
-
// {wholeTok
|
|
401
|
-
const buyUpTo = weightRanges[i].low.mul(actualLimits.low);
|
|
402
|
-
const sellDownTo = weightRanges[i].high.mul(actualLimits.high);
|
|
403
|
-
if (
|
|
404
|
-
// {USD} = {wholeTok
|
|
405
|
-
const tokenDeficitValue = buyUpTo.sub(
|
|
407
|
+
// {wholeTok} = {wholeTok/wholeBU} * {wholeBU/wholeShare} * {wholeShare}
|
|
408
|
+
const buyUpTo = weightRanges[i].low.mul(actualLimits.low).mul(supply);
|
|
409
|
+
const sellDownTo = weightRanges[i].high.mul(actualLimits.high).mul(supply);
|
|
410
|
+
if (assets[i].lt(buyUpTo)) {
|
|
411
|
+
// {USD} = {wholeTok} * {USD/wholeTok}
|
|
412
|
+
const tokenDeficitValue = buyUpTo.sub(assets[i]).mul(prices[i]);
|
|
406
413
|
// $1 minimum
|
|
407
414
|
if (tokenDeficitValue.gte(numbers_1.ONE)) {
|
|
408
415
|
deficitTokens.push(token);
|
|
409
416
|
deficitTokenSizes.push(tokenDeficitValue.toNumber());
|
|
410
417
|
}
|
|
411
418
|
}
|
|
412
|
-
else if (
|
|
413
|
-
// {USD} = {wholeTok
|
|
414
|
-
const tokenSurplusValue =
|
|
419
|
+
else if (assets[i].gt(sellDownTo)) {
|
|
420
|
+
// {USD} = {wholeTok} * {USD/wholeTok}
|
|
421
|
+
const tokenSurplusValue = assets[i].sub(sellDownTo).mul(prices[i]);
|
|
415
422
|
// $1 minimum
|
|
416
423
|
if (tokenSurplusValue.gte(numbers_1.ONE)) {
|
|
417
424
|
surplusTokens.push(token);
|
|
@@ -11,7 +11,7 @@ export interface StartRebalanceArgsPartial {
|
|
|
11
11
|
*
|
|
12
12
|
* @param _supply {share}
|
|
13
13
|
* @param tokens Addresses of tokens in the basket
|
|
14
|
-
* @param
|
|
14
|
+
* @param _assets {tok} Folio asset assets
|
|
15
15
|
* @param decimals Decimals of each token
|
|
16
16
|
* @param _targetBasket D18{1} Ideal basket
|
|
17
17
|
* @param _prices {USD/wholeTok} USD prices for each *whole* token
|
|
@@ -20,4 +20,4 @@ export interface StartRebalanceArgsPartial {
|
|
|
20
20
|
* @param weightControl TRACKING=false, NATIVE=true
|
|
21
21
|
* @param deferWeights Whether to use the full range for weights, only possible for NATIVE DTFs
|
|
22
22
|
*/
|
|
23
|
-
export declare const getStartRebalance: (_supply: bigint, tokens: string[],
|
|
23
|
+
export declare const getStartRebalance: (_supply: bigint, tokens: string[], _assets: bigint[], decimals: bigint[], _targetBasket: bigint[], _prices: number[], _priceError: number[], weightControl: boolean, deferWeights: boolean, debug?: boolean) => StartRebalanceArgsPartial;
|
package/dist/start-rebalance.js
CHANGED
|
@@ -10,7 +10,7 @@ const numbers_1 = require("./numbers");
|
|
|
10
10
|
*
|
|
11
11
|
* @param _supply {share}
|
|
12
12
|
* @param tokens Addresses of tokens in the basket
|
|
13
|
-
* @param
|
|
13
|
+
* @param _assets {tok} Folio asset assets
|
|
14
14
|
* @param decimals Decimals of each token
|
|
15
15
|
* @param _targetBasket D18{1} Ideal basket
|
|
16
16
|
* @param _prices {USD/wholeTok} USD prices for each *whole* token
|
|
@@ -19,15 +19,17 @@ const numbers_1 = require("./numbers");
|
|
|
19
19
|
* @param weightControl TRACKING=false, NATIVE=true
|
|
20
20
|
* @param deferWeights Whether to use the full range for weights, only possible for NATIVE DTFs
|
|
21
21
|
*/
|
|
22
|
-
const getStartRebalance = (_supply, tokens,
|
|
22
|
+
const getStartRebalance = (_supply, tokens, _assets, decimals, _targetBasket, _prices, _priceError, weightControl, deferWeights, debug) => {
|
|
23
23
|
if (debug) {
|
|
24
|
-
console.log("getStartRebalance", _supply, tokens,
|
|
24
|
+
console.log("getStartRebalance", _supply, tokens, _assets, decimals, _targetBasket, _prices, _priceError, weightControl, deferWeights);
|
|
25
25
|
}
|
|
26
26
|
if (deferWeights && !weightControl) {
|
|
27
27
|
throw new Error("deferWeights is not supported for tracking DTFs");
|
|
28
28
|
}
|
|
29
|
-
// {
|
|
30
|
-
const
|
|
29
|
+
// {wholeShare} = {share} / {share/wholeShare}
|
|
30
|
+
const supply = new utils_1.Decimal(_supply.toString()).div(numbers_1.D18d);
|
|
31
|
+
// {wholeTok} = {tok} * {share/wholeShare} / {tok/wholeTok} / D18
|
|
32
|
+
const assets = _assets.map((c, i) => new utils_1.Decimal(c.toString()).div(new utils_1.Decimal(`1e${decimals[i]}`)));
|
|
31
33
|
// convert price number inputs to bigints
|
|
32
34
|
// {USD/wholeTok}
|
|
33
35
|
const prices = _prices.map((a) => new utils_1.Decimal(a.toString()));
|
|
@@ -48,12 +50,12 @@ const getStartRebalance = (_supply, tokens, _folio, decimals, _targetBasket, _pr
|
|
|
48
50
|
throw new Error("price error >= 1");
|
|
49
51
|
}
|
|
50
52
|
// === newWeights ===
|
|
51
|
-
// {USD
|
|
52
|
-
const
|
|
53
|
+
// {USD} = {wholeTok} * {USD/wholeTok}
|
|
54
|
+
const dtfValue = assets
|
|
53
55
|
.map((f, i) => f.mul(prices[i]))
|
|
54
56
|
.reduce((a, b) => a.add(b));
|
|
55
|
-
// {wholeTok/wholeShare} = {1} * {USD
|
|
56
|
-
const spotWeight = targetBasket[i].mul(
|
|
57
|
+
// {wholeTok/wholeShare} = {1} * {USD} / {USD/wholeTok} / {wholeShare}
|
|
58
|
+
const spotWeight = targetBasket[i].mul(dtfValue).div(prices[i]).div(supply);
|
|
57
59
|
// D27{tok/share}{wholeShare/wholeTok} = D27 * {tok/wholeTok} / {share/wholeShare}
|
|
58
60
|
const limitMultiplier = numbers_1.D27d.mul(new utils_1.Decimal(`1e${decimals[i]}`)).div(numbers_1.D18d);
|
|
59
61
|
if (debug) {
|