@reserve-protocol/dtf-rebalance-lib 0.1.3 → 0.1.5
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 +1 -1
- package/dist/open-auction.js +75 -41
- package/dist/start-rebalance.d.ts +2 -2
- package/dist/start-rebalance.js +20 -18
- package/package.json +1 -1
package/dist/open-auction.d.ts
CHANGED
@@ -59,4 +59,4 @@ export declare const getTargetBasket: (_initialWeights: WeightRange[], _prices:
|
|
59
59
|
* @param _priceError {1} Price error to use for each token during auction pricing; should be smaller than price error during startRebalance
|
60
60
|
* @param _finalStageAt {1} The % rebalanced from the initial Folio to determine when is the final stage of the rebalance
|
61
61
|
*/
|
62
|
-
export declare const getOpenAuction: (rebalance: Rebalance, _supply: bigint, _initialFolio: bigint[] | undefined, _targetBasket: bigint[] | undefined, _folio: bigint[], _decimals: bigint[], _prices: number[], _priceError: number[], _finalStageAt: number,
|
62
|
+
export declare const getOpenAuction: (rebalance: Rebalance, _supply: bigint, _initialFolio: bigint[] | undefined, _targetBasket: bigint[] | undefined, _folio: bigint[], _decimals: bigint[], _prices: number[], _priceError: number[], _finalStageAt: number, debug?: boolean) => [OpenAuctionArgs, AuctionMetrics];
|
package/dist/open-auction.js
CHANGED
@@ -55,8 +55,8 @@ exports.getTargetBasket = getTargetBasket;
|
|
55
55
|
* @param _priceError {1} Price error to use for each token during auction pricing; should be smaller than price error during startRebalance
|
56
56
|
* @param _finalStageAt {1} The % rebalanced from the initial Folio to determine when is the final stage of the rebalance
|
57
57
|
*/
|
58
|
-
const getOpenAuction = (rebalance, _supply, _initialFolio = [], _targetBasket = [], _folio, _decimals, _prices, _priceError, _finalStageAt,
|
59
|
-
if (
|
58
|
+
const getOpenAuction = (rebalance, _supply, _initialFolio = [], _targetBasket = [], _folio, _decimals, _prices, _priceError, _finalStageAt, debug) => {
|
59
|
+
if (debug) {
|
60
60
|
console.log("getOpenAuction", rebalance, _supply, _initialFolio, _targetBasket, _folio, _decimals, _prices, _priceError, _finalStageAt);
|
61
61
|
}
|
62
62
|
if (rebalance.tokens.length != _targetBasket.length ||
|
@@ -101,13 +101,22 @@ const getOpenAuction = (rebalance, _supply, _initialFolio = [], _targetBasket =
|
|
101
101
|
// ================================================================
|
102
102
|
// calculate ideal spot limit, the actual BU<->share ratio
|
103
103
|
// {USD/wholeShare} = {wholeTok/wholeShare} * {USD/wholeTok}
|
104
|
-
const shareValue = folio
|
104
|
+
const shareValue = folio
|
105
|
+
.map((f, i) => {
|
106
|
+
return rebalance.inRebalance[i] ? f.mul(prices[i]) : numbers_1.ZERO;
|
107
|
+
})
|
108
|
+
.reduce((a, b) => a.add(b));
|
105
109
|
// {USD/wholeBU} = {wholeTok/wholeBU} * {USD/wholeTok}
|
106
110
|
const buValue = weightRanges.map((weightRange, i) => weightRange.spot.mul(prices[i])).reduce((a, b) => a.add(b));
|
107
|
-
|
111
|
+
const buPriceChange = buValue.sub(shareValue).abs().div(shareValue);
|
112
|
+
console.log(` 🧺 ${buPriceChange.mul(100).toFixed(2)}% price change`);
|
113
|
+
if (debug) {
|
108
114
|
console.log("shareValue", shareValue.toString());
|
109
115
|
console.log("buValue", buValue.toString());
|
110
116
|
}
|
117
|
+
if (buValue.div(shareValue).gt(10) || shareValue.div(buValue).gt(10)) {
|
118
|
+
throw new Error("buValue and shareValue are too different");
|
119
|
+
}
|
111
120
|
// ================================================================
|
112
121
|
// calculate rebalanceTarget
|
113
122
|
const ejectionIndices = [];
|
@@ -118,7 +127,9 @@ const getOpenAuction = (rebalance, _supply, _initialFolio = [], _targetBasket =
|
|
118
127
|
}
|
119
128
|
// {1} = {wholeTok/wholeShare} * {USD/wholeTok} / {USD/wholeShare}
|
120
129
|
const portionBeingEjected = ejectionIndices
|
121
|
-
.map((i) =>
|
130
|
+
.map((i) => {
|
131
|
+
return rebalance.inRebalance[i] ? folio[i].mul(prices[i]) : numbers_1.ZERO;
|
132
|
+
})
|
122
133
|
.reduce((a, b) => a.add(b), numbers_1.ZERO)
|
123
134
|
.div(shareValue);
|
124
135
|
// {1} = {USD/wholeShare} / {USD/wholeShare}
|
@@ -154,39 +165,44 @@ const getOpenAuction = (rebalance, _supply, _initialFolio = [], _targetBasket =
|
|
154
165
|
: progression.sub(initialProgression).div(numbers_1.ONE.sub(initialProgression));
|
155
166
|
let rebalanceTarget = numbers_1.ONE;
|
156
167
|
let round = AuctionRound.FINAL;
|
157
|
-
if (
|
168
|
+
if (debug) {
|
158
169
|
console.log("initialProgression", initialProgression.toString());
|
159
170
|
console.log("progression", progression.toString());
|
160
171
|
console.log("relativeProgression", relativeProgression.toString());
|
161
172
|
console.log("portionBeingEjected", portionBeingEjected.toString());
|
162
173
|
console.log("finalStageAt", finalStageAt.toString());
|
163
174
|
}
|
164
|
-
//
|
165
|
-
if (
|
166
|
-
round = AuctionRound.EJECT;
|
167
|
-
if (relativeProgression.lt(finalStageAt.sub(0.02))) {
|
168
|
-
rebalanceTarget = progression.add(portionBeingEjected.mul(1.1)); // set rebalanceTarget to 10% more than needed to ensure ejection completes
|
169
|
-
// do not finish trading yet
|
170
|
-
if (rebalanceTarget.gte(numbers_1.ONE)) {
|
171
|
-
rebalanceTarget = initialProgression.add(numbers_1.ONE.sub(initialProgression).mul(finalStageAt));
|
172
|
-
}
|
173
|
-
}
|
174
|
-
}
|
175
|
-
else if (relativeProgression.lt(finalStageAt.sub(0.02))) {
|
176
|
-
// wiggle room to prevent having to re-run an auction at the same stage after price movement
|
175
|
+
// approach finalStageAt first
|
176
|
+
if (progression.lt(0.99) && relativeProgression.lt(finalStageAt.sub(0.02))) {
|
177
177
|
round = AuctionRound.PROGRESS;
|
178
178
|
rebalanceTarget = initialProgression.add(numbers_1.ONE.sub(initialProgression).mul(finalStageAt));
|
179
|
+
if (rebalanceTarget.gte(0.99)) {
|
180
|
+
rebalanceTarget = numbers_1.ONE;
|
181
|
+
}
|
179
182
|
if (rebalanceTarget.eq(numbers_1.ONE)) {
|
180
183
|
round = AuctionRound.FINAL;
|
181
184
|
}
|
182
185
|
}
|
183
|
-
|
184
|
-
|
186
|
+
// EJECT
|
187
|
+
if (portionBeingEjected.gt(0)) {
|
188
|
+
round = AuctionRound.EJECT;
|
189
|
+
// if the ejections are everything that's left, keep the finalStageAt targeting from above
|
190
|
+
if (progression.add(portionBeingEjected).lt(numbers_1.ONE)) {
|
191
|
+
// else: get rid of all the dust
|
192
|
+
let ejectionTarget = progression.add(portionBeingEjected.mul(1.02)); // buy 2% extra
|
193
|
+
if (ejectionTarget.gt(numbers_1.ONE)) {
|
194
|
+
ejectionTarget = numbers_1.ONE;
|
195
|
+
}
|
196
|
+
if (ejectionTarget.gt(rebalanceTarget)) {
|
197
|
+
rebalanceTarget = ejectionTarget;
|
198
|
+
}
|
199
|
+
}
|
185
200
|
}
|
186
|
-
if (rebalanceTarget.
|
187
|
-
|
201
|
+
if (rebalanceTarget.lte(numbers_1.ZERO) || rebalanceTarget.gt(numbers_1.ONE)) {
|
202
|
+
throw new Error("something has gone very wrong");
|
188
203
|
}
|
189
|
-
if (
|
204
|
+
if (debug) {
|
205
|
+
console.log("round", round);
|
190
206
|
console.log("rebalanceTarget", rebalanceTarget.toString());
|
191
207
|
}
|
192
208
|
// {1}
|
@@ -201,11 +217,14 @@ const getOpenAuction = (rebalance, _supply, _initialFolio = [], _targetBasket =
|
|
201
217
|
spot: (0, numbers_1.bn)(spotLimit.mul(numbers_1.D18d)),
|
202
218
|
high: (0, numbers_1.bn)(spotLimit.add(spotLimit.mul(delta)).mul(numbers_1.D18d)),
|
203
219
|
};
|
204
|
-
|
205
|
-
|
206
|
-
|
220
|
+
// hold some surpluses aside if ejecting
|
221
|
+
if (round == AuctionRound.EJECT) {
|
222
|
+
// buy 0.1% more
|
223
|
+
newLimits.low += newLimits.low / 1000n;
|
224
|
+
// aim 1% higher in the future
|
225
|
+
newLimits.spot += newLimits.spot / 100n;
|
207
226
|
// leave 10% room to increase low in the future if ejection leaves dust behind
|
208
|
-
newLimits.high +=
|
227
|
+
newLimits.high += newLimits.high / 10n;
|
209
228
|
}
|
210
229
|
// low
|
211
230
|
if (newLimits.low < rebalance.limits.low) {
|
@@ -228,7 +247,7 @@ const getOpenAuction = (rebalance, _supply, _initialFolio = [], _targetBasket =
|
|
228
247
|
if (newLimits.high > rebalance.limits.high) {
|
229
248
|
newLimits.high = rebalance.limits.high;
|
230
249
|
}
|
231
|
-
if (
|
250
|
+
if (debug) {
|
232
251
|
console.log("newLimits", newLimits);
|
233
252
|
}
|
234
253
|
// ================================================================
|
@@ -257,11 +276,14 @@ const getOpenAuction = (rebalance, _supply, _initialFolio = [], _targetBasket =
|
|
257
276
|
.mul(decimalScale[i])
|
258
277
|
.div(numbers_1.D18d)),
|
259
278
|
};
|
260
|
-
|
261
|
-
|
262
|
-
|
279
|
+
// hold some surpluses aside if ejecting
|
280
|
+
if (round == AuctionRound.EJECT) {
|
281
|
+
// buy 0.1% more
|
282
|
+
newWeightsD27.low += newWeightsD27.low / 1000n;
|
283
|
+
// aim 1% higher in the future
|
284
|
+
newWeightsD27.spot += newWeightsD27.spot / 100n;
|
263
285
|
// leave 10% room to increase low in the future if ejection leaves dust behind
|
264
|
-
newWeightsD27.high +=
|
286
|
+
newWeightsD27.high += newWeightsD27.high / 10n;
|
265
287
|
}
|
266
288
|
if (newWeightsD27.low < weightRange.low) {
|
267
289
|
newWeightsD27.low = weightRange.low;
|
@@ -283,7 +305,7 @@ const getOpenAuction = (rebalance, _supply, _initialFolio = [], _targetBasket =
|
|
283
305
|
}
|
284
306
|
return newWeightsD27;
|
285
307
|
});
|
286
|
-
if (
|
308
|
+
if (debug) {
|
287
309
|
console.log("newWeights", newWeights);
|
288
310
|
}
|
289
311
|
// ================================================================
|
@@ -293,7 +315,7 @@ const getOpenAuction = (rebalance, _supply, _initialFolio = [], _targetBasket =
|
|
293
315
|
// revert if price out of bounds
|
294
316
|
const spotPrice = (0, numbers_1.bn)(prices[i].mul(numbers_1.D27d).div(decimalScale[i]));
|
295
317
|
if (spotPrice < initialPrice.low || spotPrice > initialPrice.high) {
|
296
|
-
throw new Error(
|
318
|
+
throw new Error(`spot price ${spotPrice.toString()} out of bounds relative to initial range [${initialPrice.low.toString()}, ${initialPrice.high.toString()}]! auction launcher MUST closeRebalance to prevent loss!`);
|
297
319
|
}
|
298
320
|
if (rebalance.priceControl == types_1.PriceControl.NONE) {
|
299
321
|
return initialPrice;
|
@@ -322,7 +344,7 @@ const getOpenAuction = (rebalance, _supply, _initialFolio = [], _targetBasket =
|
|
322
344
|
}
|
323
345
|
return pricesD27;
|
324
346
|
});
|
325
|
-
if (
|
347
|
+
if (debug) {
|
326
348
|
console.log("newPrices", newPrices);
|
327
349
|
}
|
328
350
|
// ================================================================
|
@@ -344,19 +366,31 @@ const getOpenAuction = (rebalance, _supply, _initialFolio = [], _targetBasket =
|
|
344
366
|
// {wholeTok/wholeShare} = {wholeTok/wholeBU} * {wholeBU/wholeShare}
|
345
367
|
const buyUpTo = weightRanges[i].low.mul(actualLimits.low);
|
346
368
|
const sellDownTo = weightRanges[i].high.mul(actualLimits.high);
|
347
|
-
if (folio[i].lt(buyUpTo)) {
|
369
|
+
if (rebalance.inRebalance[i] && folio[i].lt(buyUpTo)) {
|
348
370
|
deficitTokens.push(token);
|
349
371
|
}
|
350
|
-
else if (folio[i].gt(sellDownTo)) {
|
372
|
+
else if (rebalance.inRebalance[i] && folio[i].gt(sellDownTo)) {
|
351
373
|
surplusTokens.push(token);
|
352
374
|
}
|
353
375
|
});
|
376
|
+
// ================================================================
|
377
|
+
// only return tokens that are in the rebalance
|
378
|
+
const returnTokens = [];
|
379
|
+
const returnWeights = [];
|
380
|
+
const returnPrices = [];
|
381
|
+
for (let i = 0; i < rebalance.tokens.length; i++) {
|
382
|
+
if (rebalance.inRebalance[i]) {
|
383
|
+
returnTokens.push(rebalance.tokens[i]);
|
384
|
+
returnWeights.push(newWeights[i]);
|
385
|
+
returnPrices.push(newPrices[i]);
|
386
|
+
}
|
387
|
+
}
|
354
388
|
return [
|
355
389
|
{
|
356
390
|
rebalanceNonce: rebalance.nonce,
|
357
|
-
tokens:
|
358
|
-
newWeights:
|
359
|
-
newPrices:
|
391
|
+
tokens: returnTokens,
|
392
|
+
newWeights: returnWeights,
|
393
|
+
newPrices: returnPrices,
|
360
394
|
newLimits: newLimits,
|
361
395
|
},
|
362
396
|
{
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { PriceRange, RebalanceLimits, WeightRange } from
|
1
|
+
import { PriceRange, RebalanceLimits, WeightRange } from "./types";
|
2
2
|
export interface StartRebalanceArgsPartial {
|
3
3
|
weights: WeightRange[];
|
4
4
|
prices: PriceRange[];
|
@@ -19,4 +19,4 @@ export interface StartRebalanceArgsPartial {
|
|
19
19
|
* @param _dtfPrice {USD/wholeShare} DTF price
|
20
20
|
* @param weightControl TRACKING=false, NATIVE=true
|
21
21
|
*/
|
22
|
-
export declare const getStartRebalance: (_supply: bigint, tokens: string[], _folio: bigint[], decimals: bigint[], _targetBasket: bigint[], _prices: number[], _priceError: number[], weightControl: boolean,
|
22
|
+
export declare const getStartRebalance: (_supply: bigint, tokens: string[], _folio: bigint[], decimals: bigint[], _targetBasket: bigint[], _prices: number[], _priceError: number[], weightControl: boolean, debug?: boolean) => StartRebalanceArgsPartial;
|
package/dist/start-rebalance.js
CHANGED
@@ -21,9 +21,9 @@ const numbers_1 = require("./numbers");
|
|
21
21
|
* @param _dtfPrice {USD/wholeShare} DTF price
|
22
22
|
* @param weightControl TRACKING=false, NATIVE=true
|
23
23
|
*/
|
24
|
-
const getStartRebalance = (_supply, tokens, _folio, decimals, _targetBasket, _prices, _priceError, weightControl,
|
25
|
-
if (
|
26
|
-
console.log(
|
24
|
+
const getStartRebalance = (_supply, tokens, _folio, decimals, _targetBasket, _prices, _priceError, weightControl, debug) => {
|
25
|
+
if (debug) {
|
26
|
+
console.log("getStartRebalance", _supply, tokens, _folio, decimals, _targetBasket, _prices, _priceError, weightControl);
|
27
27
|
}
|
28
28
|
// {wholeTok/wholeShare} = D18{tok/share} * {share/wholeShare} / {tok/wholeTok} / D18
|
29
29
|
const folio = _folio.map((c, i) => new decimal_js_light_1.default(c.toString()).div(new decimal_js_light_1.default(`1e${decimals[i]}`)));
|
@@ -43,24 +43,26 @@ const getStartRebalance = (_supply, tokens, _folio, decimals, _targetBasket, _pr
|
|
43
43
|
const newWeights = [];
|
44
44
|
const newPrices = [];
|
45
45
|
const newLimits = {
|
46
|
-
low: (0, numbers_1.bn)(
|
47
|
-
spot: (0, numbers_1.bn)(
|
48
|
-
high: (0, numbers_1.bn)(
|
46
|
+
low: (0, numbers_1.bn)("1e18"),
|
47
|
+
spot: (0, numbers_1.bn)("1e18"),
|
48
|
+
high: (0, numbers_1.bn)("1e18"),
|
49
49
|
};
|
50
50
|
// ================================================================
|
51
51
|
for (let i = 0; i < tokens.length; i++) {
|
52
52
|
if (priceError[i].gte(numbers_1.ONE)) {
|
53
|
-
throw new Error(
|
53
|
+
throw new Error("cannot defer prices");
|
54
54
|
}
|
55
55
|
// === newWeights ===
|
56
56
|
// {USD/wholeShare} = {wholeTok/wholeShare} * {USD/wholeTok}
|
57
|
-
const dtfPrice = folio
|
57
|
+
const dtfPrice = folio
|
58
|
+
.map((f, i) => f.mul(prices[i]))
|
59
|
+
.reduce((a, b) => a.add(b));
|
58
60
|
// {wholeTok/wholeShare} = {1} * {USD/wholeShare} / {USD/wholeTok}
|
59
61
|
const spotWeight = targetBasket[i].mul(dtfPrice).div(prices[i]);
|
60
62
|
// D27{tok/share}{wholeShare/wholeTok} = D27 * {tok/wholeTok} / {share/wholeShare}
|
61
63
|
const limitMultiplier = numbers_1.D27d.mul(new decimal_js_light_1.default(`1e${decimals[i]}`)).div(numbers_1.D18d);
|
62
|
-
if (
|
63
|
-
console.log(
|
64
|
+
if (debug) {
|
65
|
+
console.log("limitMultiplier", limitMultiplier.toString());
|
64
66
|
}
|
65
67
|
if (!weightControl) {
|
66
68
|
// D27{tok/BU} = {wholeTok/wholeShare} * D27{tok/share}{wholeShare/wholeTok} / {BU/share}
|
@@ -73,8 +75,8 @@ const getStartRebalance = (_supply, tokens, _folio, decimals, _targetBasket, _pr
|
|
73
75
|
else {
|
74
76
|
// NATIVE case
|
75
77
|
// {wholeTok/wholeShare} = {wholeTok/wholeShare} / {1}
|
76
|
-
const lowWeight = spotWeight.mul(numbers_1.ONE.
|
77
|
-
const highWeight = spotWeight.
|
78
|
+
const lowWeight = spotWeight.mul(numbers_1.ONE.sub(priceError[i]));
|
79
|
+
const highWeight = spotWeight.div(numbers_1.ONE.sub(priceError[i]));
|
78
80
|
// D27{tok/share} = {wholeTok/wholeShare} * D27{tok/share}{wholeShare/wholeTok} / {BU/share}
|
79
81
|
newWeights.push({
|
80
82
|
low: (0, numbers_1.bn)(lowWeight.mul(limitMultiplier)),
|
@@ -101,16 +103,16 @@ const getStartRebalance = (_supply, tokens, _folio, decimals, _targetBasket, _pr
|
|
101
103
|
.map((portion, i) => portion.mul(priceError[i]))
|
102
104
|
.reduce((a, b) => a.add(b));
|
103
105
|
if (totalPortion.gte(numbers_1.ONE)) {
|
104
|
-
throw new Error(
|
106
|
+
throw new Error("totalPortion > 1");
|
105
107
|
}
|
106
108
|
// D18{BU/share} = {1} * D18 * {BU/share}
|
107
|
-
newLimits.low = (0, numbers_1.bn)(numbers_1.ONE.
|
108
|
-
newLimits.high = (0, numbers_1.bn)(numbers_1.ONE.
|
109
|
+
newLimits.low = (0, numbers_1.bn)(numbers_1.ONE.sub(totalPortion).mul(numbers_1.D18d));
|
110
|
+
newLimits.high = (0, numbers_1.bn)(numbers_1.ONE.div(numbers_1.ONE.sub(totalPortion)).mul(numbers_1.D18d));
|
109
111
|
}
|
110
|
-
if (
|
112
|
+
if (debug) {
|
111
113
|
console.log("newWeights", newWeights);
|
112
|
-
console.log(
|
113
|
-
console.log(
|
114
|
+
console.log("newPrices", newPrices);
|
115
|
+
console.log("newLimits", newLimits);
|
114
116
|
}
|
115
117
|
return {
|
116
118
|
weights: newWeights,
|