@reserve-protocol/dtf-rebalance-lib 0.1.3 → 0.1.4
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 +86 -40
- package/dist/start-rebalance.d.ts +2 -2
- package/dist/start-rebalance.js +16 -14
- 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,20 @@ 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
|
-
if (
|
111
|
+
if (debug) {
|
108
112
|
console.log("shareValue", shareValue.toString());
|
109
113
|
console.log("buValue", buValue.toString());
|
110
114
|
}
|
115
|
+
if (buValue.div(shareValue).gt(10) || shareValue.div(buValue).gt(100)) {
|
116
|
+
throw new Error("buValue and shareValue are too different");
|
117
|
+
}
|
111
118
|
// ================================================================
|
112
119
|
// calculate rebalanceTarget
|
113
120
|
const ejectionIndices = [];
|
@@ -118,7 +125,9 @@ const getOpenAuction = (rebalance, _supply, _initialFolio = [], _targetBasket =
|
|
118
125
|
}
|
119
126
|
// {1} = {wholeTok/wholeShare} * {USD/wholeTok} / {USD/wholeShare}
|
120
127
|
const portionBeingEjected = ejectionIndices
|
121
|
-
.map((i) =>
|
128
|
+
.map((i) => {
|
129
|
+
return rebalance.inRebalance[i] ? folio[i].mul(prices[i]) : numbers_1.ZERO;
|
130
|
+
})
|
122
131
|
.reduce((a, b) => a.add(b), numbers_1.ZERO)
|
123
132
|
.div(shareValue);
|
124
133
|
// {1} = {USD/wholeShare} / {USD/wholeShare}
|
@@ -154,39 +163,44 @@ const getOpenAuction = (rebalance, _supply, _initialFolio = [], _targetBasket =
|
|
154
163
|
: progression.sub(initialProgression).div(numbers_1.ONE.sub(initialProgression));
|
155
164
|
let rebalanceTarget = numbers_1.ONE;
|
156
165
|
let round = AuctionRound.FINAL;
|
157
|
-
if (
|
166
|
+
if (debug) {
|
158
167
|
console.log("initialProgression", initialProgression.toString());
|
159
168
|
console.log("progression", progression.toString());
|
160
169
|
console.log("relativeProgression", relativeProgression.toString());
|
161
170
|
console.log("portionBeingEjected", portionBeingEjected.toString());
|
162
171
|
console.log("finalStageAt", finalStageAt.toString());
|
163
172
|
}
|
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
|
173
|
+
// approach finalStageAt first
|
174
|
+
if (progression.lt(0.99) && relativeProgression.lt(finalStageAt.sub(0.02))) {
|
177
175
|
round = AuctionRound.PROGRESS;
|
178
176
|
rebalanceTarget = initialProgression.add(numbers_1.ONE.sub(initialProgression).mul(finalStageAt));
|
177
|
+
if (numbers_1.ONE.sub(rebalanceTarget).lt(0.99)) {
|
178
|
+
rebalanceTarget = numbers_1.ONE;
|
179
|
+
}
|
179
180
|
if (rebalanceTarget.eq(numbers_1.ONE)) {
|
180
181
|
round = AuctionRound.FINAL;
|
181
182
|
}
|
182
183
|
}
|
183
|
-
|
184
|
-
|
184
|
+
// EJECT
|
185
|
+
if (portionBeingEjected.gt(0)) {
|
186
|
+
round = AuctionRound.EJECT;
|
187
|
+
// if the ejections are everything that's left, keep the finalStageAt targeting from above
|
188
|
+
if (progression.add(portionBeingEjected).lt(numbers_1.ONE)) {
|
189
|
+
// else: get rid of all the dust
|
190
|
+
let ejectionTarget = progression.add(portionBeingEjected.mul(1.02)); // buy 2% extra
|
191
|
+
if (ejectionTarget.gt(numbers_1.ONE)) {
|
192
|
+
ejectionTarget = numbers_1.ONE;
|
193
|
+
}
|
194
|
+
if (ejectionTarget.gt(rebalanceTarget)) {
|
195
|
+
rebalanceTarget = ejectionTarget;
|
196
|
+
}
|
197
|
+
}
|
185
198
|
}
|
186
|
-
if (rebalanceTarget.
|
187
|
-
|
199
|
+
if (rebalanceTarget.lte(numbers_1.ZERO) || rebalanceTarget.gt(numbers_1.ONE)) {
|
200
|
+
throw new Error("something has gone very wrong");
|
188
201
|
}
|
189
|
-
if (
|
202
|
+
if (debug) {
|
203
|
+
console.log("round", round);
|
190
204
|
console.log("rebalanceTarget", rebalanceTarget.toString());
|
191
205
|
}
|
192
206
|
// {1}
|
@@ -201,11 +215,21 @@ const getOpenAuction = (rebalance, _supply, _initialFolio = [], _targetBasket =
|
|
201
215
|
spot: (0, numbers_1.bn)(spotLimit.mul(numbers_1.D18d)),
|
202
216
|
high: (0, numbers_1.bn)(spotLimit.add(spotLimit.mul(delta)).mul(numbers_1.D18d)),
|
203
217
|
};
|
204
|
-
|
205
|
-
|
206
|
-
|
218
|
+
// hold some surpluses aside if ejecting
|
219
|
+
if (round == AuctionRound.EJECT) {
|
220
|
+
// buy 0.1% more
|
221
|
+
newLimits.low += newLimits.low / 1000n;
|
222
|
+
// aim 1% higher
|
223
|
+
newLimits.spot += newLimits.spot / 100n;
|
207
224
|
// leave 10% room to increase low in the future if ejection leaves dust behind
|
208
|
-
newLimits.high +=
|
225
|
+
newLimits.high += newLimits.high / 10n;
|
226
|
+
}
|
227
|
+
else if (round == AuctionRound.FINAL && progression.gt(0.999)) {
|
228
|
+
// if it's the final round and we're within 0.1%, buy 10% more than we need to
|
229
|
+
const delta = (0, numbers_1.bn)(numbers_1.ONE.sub(progression).mul(numbers_1.D18d).div(10));
|
230
|
+
newLimits.low += (newLimits.low * delta) / 10n ** 18n;
|
231
|
+
newLimits.spot += (newLimits.spot * delta) / 10n ** 18n;
|
232
|
+
newLimits.high += (newLimits.high * delta) / 10n ** 18n;
|
209
233
|
}
|
210
234
|
// low
|
211
235
|
if (newLimits.low < rebalance.limits.low) {
|
@@ -228,7 +252,7 @@ const getOpenAuction = (rebalance, _supply, _initialFolio = [], _targetBasket =
|
|
228
252
|
if (newLimits.high > rebalance.limits.high) {
|
229
253
|
newLimits.high = rebalance.limits.high;
|
230
254
|
}
|
231
|
-
if (
|
255
|
+
if (debug) {
|
232
256
|
console.log("newLimits", newLimits);
|
233
257
|
}
|
234
258
|
// ================================================================
|
@@ -257,11 +281,21 @@ const getOpenAuction = (rebalance, _supply, _initialFolio = [], _targetBasket =
|
|
257
281
|
.mul(decimalScale[i])
|
258
282
|
.div(numbers_1.D18d)),
|
259
283
|
};
|
260
|
-
|
261
|
-
|
262
|
-
|
284
|
+
// hold some surpluses aside if ejecting
|
285
|
+
if (round == AuctionRound.EJECT) {
|
286
|
+
// buy 0.1% more
|
287
|
+
newWeightsD27.low += newWeightsD27.low / 1000n;
|
288
|
+
// aim 1% higher
|
289
|
+
newWeightsD27.spot += newWeightsD27.spot / 100n;
|
263
290
|
// leave 10% room to increase low in the future if ejection leaves dust behind
|
264
|
-
newWeightsD27.high +=
|
291
|
+
newWeightsD27.high += newWeightsD27.high / 10n;
|
292
|
+
}
|
293
|
+
else if (round == AuctionRound.FINAL && progression.gt(0.999)) {
|
294
|
+
// if it's the final round and we're within 0.1%, buy 10% more than we need to
|
295
|
+
const delta = (0, numbers_1.bn)(numbers_1.ONE.sub(progression).mul(numbers_1.D18d).div(10));
|
296
|
+
newWeightsD27.low += (newWeightsD27.low * delta) / 10n ** 18n;
|
297
|
+
newWeightsD27.spot += (newWeightsD27.spot * delta) / 10n ** 18n;
|
298
|
+
newWeightsD27.high += (newWeightsD27.high * delta) / 10n ** 18n;
|
265
299
|
}
|
266
300
|
if (newWeightsD27.low < weightRange.low) {
|
267
301
|
newWeightsD27.low = weightRange.low;
|
@@ -283,7 +317,7 @@ const getOpenAuction = (rebalance, _supply, _initialFolio = [], _targetBasket =
|
|
283
317
|
}
|
284
318
|
return newWeightsD27;
|
285
319
|
});
|
286
|
-
if (
|
320
|
+
if (debug) {
|
287
321
|
console.log("newWeights", newWeights);
|
288
322
|
}
|
289
323
|
// ================================================================
|
@@ -322,7 +356,7 @@ const getOpenAuction = (rebalance, _supply, _initialFolio = [], _targetBasket =
|
|
322
356
|
}
|
323
357
|
return pricesD27;
|
324
358
|
});
|
325
|
-
if (
|
359
|
+
if (debug) {
|
326
360
|
console.log("newPrices", newPrices);
|
327
361
|
}
|
328
362
|
// ================================================================
|
@@ -344,19 +378,31 @@ const getOpenAuction = (rebalance, _supply, _initialFolio = [], _targetBasket =
|
|
344
378
|
// {wholeTok/wholeShare} = {wholeTok/wholeBU} * {wholeBU/wholeShare}
|
345
379
|
const buyUpTo = weightRanges[i].low.mul(actualLimits.low);
|
346
380
|
const sellDownTo = weightRanges[i].high.mul(actualLimits.high);
|
347
|
-
if (folio[i].lt(buyUpTo)) {
|
381
|
+
if (rebalance.inRebalance[i] && folio[i].lt(buyUpTo)) {
|
348
382
|
deficitTokens.push(token);
|
349
383
|
}
|
350
|
-
else if (folio[i].gt(sellDownTo)) {
|
384
|
+
else if (rebalance.inRebalance[i] && folio[i].gt(sellDownTo)) {
|
351
385
|
surplusTokens.push(token);
|
352
386
|
}
|
353
387
|
});
|
388
|
+
// ================================================================
|
389
|
+
// only return tokens that are in the rebalance
|
390
|
+
const returnTokens = [];
|
391
|
+
const returnWeights = [];
|
392
|
+
const returnPrices = [];
|
393
|
+
for (let i = 0; i < rebalance.tokens.length; i++) {
|
394
|
+
if (rebalance.inRebalance[i]) {
|
395
|
+
returnTokens.push(rebalance.tokens[i]);
|
396
|
+
returnWeights.push(newWeights[i]);
|
397
|
+
returnPrices.push(newPrices[i]);
|
398
|
+
}
|
399
|
+
}
|
354
400
|
return [
|
355
401
|
{
|
356
402
|
rebalanceNonce: rebalance.nonce,
|
357
|
-
tokens:
|
358
|
-
newWeights:
|
359
|
-
newPrices:
|
403
|
+
tokens: returnTokens,
|
404
|
+
newWeights: returnWeights,
|
405
|
+
newPrices: returnPrices,
|
360
406
|
newLimits: newLimits,
|
361
407
|
},
|
362
408
|
{
|
@@ -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}
|
@@ -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
109
|
newLimits.low = (0, numbers_1.bn)(numbers_1.ONE.div(numbers_1.ONE.add(totalPortion)).mul(numbers_1.D18d));
|
108
110
|
newLimits.high = (0, numbers_1.bn)(numbers_1.ONE.add(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,
|