@reserve-protocol/dtf-rebalance-lib 3.2.1 → 3.3.1
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/README.md +94 -1
- package/dist/5.0.0/open-auction.d.ts +2 -1
- package/dist/5.0.0/start-rebalance.d.ts +1 -1
- package/dist/5.0.0/types.d.ts +24 -0
- package/dist/5.0.0/types.js +3 -0
- package/dist/6.0.0/open-auction.d.ts +9 -0
- package/dist/6.0.0/open-auction.js +24 -0
- package/dist/6.0.0/start-rebalance.d.ts +8 -0
- package/dist/6.0.0/start-rebalance.js +29 -0
- package/dist/6.0.0/types.d.ts +6 -0
- package/dist/6.0.0/types.js +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/open-auction.d.ts +3 -2
- package/dist/open-auction.js +11 -11
- package/dist/start-rebalance.d.ts +4 -3
- package/dist/start-rebalance.js +12 -12
- package/dist/types.d.ts +3 -1
- package/dist/types.js +1 -0
- package/docs/auction-algorithm.md +357 -0
- package/package.json +15 -4
package/README.md
CHANGED
|
@@ -1,3 +1,96 @@
|
|
|
1
1
|
# dtf-rebalance-lib
|
|
2
2
|
|
|
3
|
-
Rebalancing library for DTFs in typescript.
|
|
3
|
+
Rebalancing library for DTFs in typescript. Computes the parameters needed to rebalance a DTF portfolio through a series of on-chain auctions, converging from its current composition toward a target basket.
|
|
4
|
+
|
|
5
|
+
For detailed formulas and worked examples, see [docs/auction-algorithm.md](docs/auction-algorithm.md).
|
|
6
|
+
|
|
7
|
+
## Repository layout
|
|
8
|
+
|
|
9
|
+
The root package, `@reserve-protocol/dtf-rebalance-lib`, is the SDK-independent core and the only package released from this repository. It stays deterministic and does not depend on `@reserve-protocol/sdk`, RPCs, subgraphs, Reserve API, Hardhat, or deployed DTF metadata.
|
|
10
|
+
|
|
11
|
+
Internal SDK-aware operational code lives under `packages/tools`. It is a private workspace used by repo-local scripts, fork tests, and Hardhat task adapters. It is not published to npm and is not part of the `@reserve-protocol/dtf-rebalance-lib` release artifact.
|
|
12
|
+
|
|
13
|
+
## How rebalancing works
|
|
14
|
+
|
|
15
|
+
Rebalancing is a two-phase process:
|
|
16
|
+
|
|
17
|
+
1. **Start** -- Call `getStartRebalance()` once to open a new rebalance. It computes initial weight ranges, price ranges, limits, and per-token auction size caps based on the current portfolio, target basket, and market prices.
|
|
18
|
+
|
|
19
|
+
2. **Auction rounds** -- Call `getOpenAuction()` repeatedly (once per round) to produce tightening parameters that progressively move the portfolio toward its target. Each round narrows the weight/price bounds and advances a _progression_ metric from 0 toward 1.
|
|
20
|
+
|
|
21
|
+
## Key concepts
|
|
22
|
+
|
|
23
|
+
**Weights and limits.** Each token's expected balance per share is `weight * limit`. Weights describe _how much_ of each token a basket unit contains; limits describe _how many_ basket units a share is worth.
|
|
24
|
+
|
|
25
|
+
**Low/high bounds.** Every weight and limit has a low, spot, and high value. The low bound defines what you buy _up to_ and the high bound defines what you sell _down to_, creating a corridor within which the auction clears.
|
|
26
|
+
|
|
27
|
+
**Progression.** A 0-to-1 metric measuring how close the portfolio is to its target composition. Each auction round advances progression by a controlled step, and the final round pushes it to 1.
|
|
28
|
+
|
|
29
|
+
**maxAuctionSize.** Caps the USD value each token can trade in a single auction round, limiting market impact. Set per-token in `getStartRebalance()`.
|
|
30
|
+
|
|
31
|
+
## Auction rounds
|
|
32
|
+
|
|
33
|
+
Each call to `getOpenAuction()` produces one of three round types:
|
|
34
|
+
|
|
35
|
+
- **EJECT** -- Removes tokens that are being dropped from the basket entirely (weight target is zero). Runs first if applicable.
|
|
36
|
+
- **PROGRESS** -- The main phase. Moves the portfolio toward the target in controlled steps, with each step bounded by `maxAuctionSize` and a progression target.
|
|
37
|
+
- **FINAL** -- Once progression crosses the `finalStageAt` threshold, tightens bounds to zero spread and finishes the rebalance.
|
|
38
|
+
|
|
39
|
+
## Tracking vs Native
|
|
40
|
+
|
|
41
|
+
**Tracking rebalances** (`weightControl = false`) keep weights fixed and move only the limits. This changes the _scale_ of the portfolio -- how many basket units each share represents -- without changing composition. The target basket is computed from current market prices.
|
|
42
|
+
|
|
43
|
+
**Native rebalances** (`weightControl = true`) keep limits fixed and move only the weights. This changes the _composition_ of the portfolio -- which tokens and in what proportions -- without changing scale. The target basket is computed from the prices at rebalance start.
|
|
44
|
+
|
|
45
|
+
## Parameters
|
|
46
|
+
|
|
47
|
+
### `getStartRebalance()`
|
|
48
|
+
|
|
49
|
+
| Parameter | Type | Description |
|
|
50
|
+
| --- | --- | --- |
|
|
51
|
+
| `version` | `FolioVersion` | Protocol version (`V4`, `V5`, or `V6`) |
|
|
52
|
+
| `_supply` | `bigint` | Current total share supply |
|
|
53
|
+
| `tokens` | `string[]` | Token addresses in the basket |
|
|
54
|
+
| `_assets` | `bigint[]` | Current token balances |
|
|
55
|
+
| `decimals` | `bigint[]` | Decimals for each token |
|
|
56
|
+
| `_targetBasket` | `bigint[]` | D18 ideal basket proportions |
|
|
57
|
+
| `_prices` | `number[]` | USD price per whole token |
|
|
58
|
+
| `_priceError` | `number[]` | Price error fraction per token |
|
|
59
|
+
| `_maxAuctionSizes` | `number[]` | Max USD auction size per token (`V5`/`V6`; ignored by `V4`) |
|
|
60
|
+
| `weightControl` | `boolean` | `false` = tracking, `true` = native |
|
|
61
|
+
| `deferWeights` | `boolean` | Use full weight range (native only) |
|
|
62
|
+
| `debug` | `boolean?` | Log debug output |
|
|
63
|
+
|
|
64
|
+
**Returns** `StartRebalanceArgsPartial` -- contains `tokens` (with weight ranges, price ranges, and max auction sizes per token for `V5`/`V6`) and `limits` (low/spot/high). Folio `V6` additionally requires the caller to pass the expected rebalance nonce to the on-chain `startRebalance()` call.
|
|
65
|
+
|
|
66
|
+
### `getOpenAuction()`
|
|
67
|
+
|
|
68
|
+
| Parameter | Type | Description |
|
|
69
|
+
| --- | --- | --- |
|
|
70
|
+
| `version` | `FolioVersion` | Protocol version |
|
|
71
|
+
| `_rebalance` | `Rebalance` | On-chain rebalance state |
|
|
72
|
+
| `_supply` | `bigint` | Current total share supply |
|
|
73
|
+
| `_initialSupply` | `bigint` | Supply at rebalance start |
|
|
74
|
+
| `_initialAssets` | `bigint[]` | Token balances at rebalance start |
|
|
75
|
+
| `_targetBasket` | `bigint[]` | D18 ideal basket proportions |
|
|
76
|
+
| `_assets` | `bigint[]` | Current token balances |
|
|
77
|
+
| `_decimals` | `bigint[]` | Token decimals |
|
|
78
|
+
| `_prices` | `number[]` | Current USD prices per whole token |
|
|
79
|
+
| `_priceError` | `number[]` | Price error fraction per token |
|
|
80
|
+
| `_finalStageAt` | `number` | Progression threshold to enter FINAL (e.g. 0.9) |
|
|
81
|
+
| `debug` | `boolean?` | Log debug output |
|
|
82
|
+
| `_auctionLength` | `bigint?` | Required for `V6`; omitted for `V4`/`V5` |
|
|
83
|
+
|
|
84
|
+
**Returns** `[OpenAuctionArgs, AuctionMetrics]` -- the on-chain call arguments and a metrics object describing the round type, progression, and per-token surplus/deficit sizes. For `V6`, `OpenAuctionArgs.auctionLength` is included and must be supplied to the on-chain `openAuction()` call.
|
|
85
|
+
|
|
86
|
+
## Version Notes
|
|
87
|
+
|
|
88
|
+
The default exported `Rebalance` and `StartRebalanceArgsPartial` types match the `V5` shape for compatibility. Version-specific aliases are also exported: `RebalanceV4`, `RebalanceV5`, `RebalanceV6`, `StartRebalanceArgsPartialV4`, `StartRebalanceArgsPartialV5`, and `StartRebalanceArgsPartialV6`.
|
|
89
|
+
|
|
90
|
+
Folio `V6` uses the same rebalance math as `V5`, plus protocol ABI differences: `startRebalance()` requires an expected nonce and `openAuction()` requires `auctionLength`.
|
|
91
|
+
|
|
92
|
+
## Utility functions
|
|
93
|
+
|
|
94
|
+
- **`getTargetBasket(weights, prices, decimals)`** -- Computes the D18 target basket proportions from initial weights and prices.
|
|
95
|
+
- **`getBasketDistribution(balances, prices, decimals)`** -- Returns the D18 value distribution across tokens given current balances and prices.
|
|
96
|
+
- **`getBasketAccuracy(balances, prices, decimals, weights)`** -- Returns a 0-to-1 score measuring how closely current balances match the target weights.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { PriceRange, RebalanceLimits, WeightRange, PriceControl } from "../types";
|
|
2
|
+
export interface TokenRebalanceParams {
|
|
3
|
+
token: string;
|
|
4
|
+
weight: WeightRange;
|
|
5
|
+
price: PriceRange;
|
|
6
|
+
maxAuctionSize: bigint;
|
|
7
|
+
inRebalance: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface RebalanceTimestamps {
|
|
10
|
+
startedAt: bigint;
|
|
11
|
+
restrictedUntil: bigint;
|
|
12
|
+
availableUntil: bigint;
|
|
13
|
+
}
|
|
14
|
+
export interface Rebalance {
|
|
15
|
+
nonce: bigint;
|
|
16
|
+
priceControl: PriceControl;
|
|
17
|
+
tokens: TokenRebalanceParams[];
|
|
18
|
+
limits: RebalanceLimits;
|
|
19
|
+
timestamps: RebalanceTimestamps;
|
|
20
|
+
}
|
|
21
|
+
export interface StartRebalanceArgsPartial {
|
|
22
|
+
tokens: TokenRebalanceParams[];
|
|
23
|
+
limits: RebalanceLimits;
|
|
24
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { AuctionMetrics, OpenAuctionArgs } from "../types";
|
|
2
|
+
import { Rebalance } from "./types";
|
|
3
|
+
/**
|
|
4
|
+
* Get the values needed to call Folio 6.0.0 `openAuction()`.
|
|
5
|
+
*
|
|
6
|
+
* 6.0.0 uses the same auction parameter math as 5.0.0; the on-chain ABI additionally
|
|
7
|
+
* requires an auction length. If omitted, callers should provide one before submitting.
|
|
8
|
+
*/
|
|
9
|
+
export declare const getOpenAuction: (rebalance: Rebalance, _supply: bigint, _initialSupply: bigint, _initialAssets: bigint[] | undefined, _targetBasket: bigint[] | undefined, _assets: bigint[], _decimals: bigint[], _prices: number[], _priceError: number[], _finalStageAt: number, debug?: boolean, _auctionLength?: bigint) => [OpenAuctionArgs, AuctionMetrics];
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getOpenAuction = void 0;
|
|
4
|
+
const open_auction_1 = require("../5.0.0/open-auction");
|
|
5
|
+
/**
|
|
6
|
+
* Get the values needed to call Folio 6.0.0 `openAuction()`.
|
|
7
|
+
*
|
|
8
|
+
* 6.0.0 uses the same auction parameter math as 5.0.0; the on-chain ABI additionally
|
|
9
|
+
* requires an auction length. If omitted, callers should provide one before submitting.
|
|
10
|
+
*/
|
|
11
|
+
const getOpenAuction = (rebalance, _supply, _initialSupply, _initialAssets = [], _targetBasket = [], _assets, _decimals, _prices, _priceError, _finalStageAt, debug, _auctionLength) => {
|
|
12
|
+
if (_auctionLength === undefined) {
|
|
13
|
+
throw new Error("Folio 6.0.0 openAuction requires auctionLength");
|
|
14
|
+
}
|
|
15
|
+
const [openAuctionArgs, auctionMetrics] = (0, open_auction_1.getOpenAuction)(rebalance, _supply, _initialSupply, _initialAssets, _targetBasket, _assets, _decimals, _prices, _priceError, _finalStageAt, debug);
|
|
16
|
+
return [
|
|
17
|
+
{
|
|
18
|
+
...openAuctionArgs,
|
|
19
|
+
auctionLength: _auctionLength,
|
|
20
|
+
},
|
|
21
|
+
auctionMetrics,
|
|
22
|
+
];
|
|
23
|
+
};
|
|
24
|
+
exports.getOpenAuction = getOpenAuction;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { StartRebalanceArgsPartial } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Get the arguments needed to call Folio 6.0.0 startRebalance.
|
|
4
|
+
*
|
|
5
|
+
* 6.0.0 uses the same token rebalance parameter math as 5.0.0; the on-chain ABI
|
|
6
|
+
* additionally requires the expected rebalance nonce, which is supplied by the caller.
|
|
7
|
+
*/
|
|
8
|
+
export declare const getStartRebalance: (_supply: bigint, tokens: string[], _assets: bigint[], decimals: bigint[], _targetBasket: bigint[], _prices: number[], _priceError: number[], _maxAuctionSizes: number[], weightControl: boolean, deferWeights: boolean, debug?: boolean) => StartRebalanceArgsPartial;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getStartRebalance = void 0;
|
|
4
|
+
const start_rebalance_1 = require("../5.0.0/start-rebalance");
|
|
5
|
+
/**
|
|
6
|
+
* Get the arguments needed to call Folio 6.0.0 startRebalance.
|
|
7
|
+
*
|
|
8
|
+
* 6.0.0 uses the same token rebalance parameter math as 5.0.0; the on-chain ABI
|
|
9
|
+
* additionally requires the expected rebalance nonce, which is supplied by the caller.
|
|
10
|
+
*/
|
|
11
|
+
const getStartRebalance = (_supply, tokens, _assets, decimals, _targetBasket, _prices, _priceError, _maxAuctionSizes, weightControl, deferWeights, debug) => {
|
|
12
|
+
const args = (0, start_rebalance_1.getStartRebalance)(_supply, tokens, _assets, decimals, _targetBasket, _prices, _priceError, _maxAuctionSizes, weightControl, deferWeights, debug);
|
|
13
|
+
if (!deferWeights || !weightControl) {
|
|
14
|
+
return args;
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
...args,
|
|
18
|
+
tokens: args.tokens.map((tokenParams, i) => ({
|
|
19
|
+
...tokenParams,
|
|
20
|
+
weight: _targetBasket[i] === 0n
|
|
21
|
+
? {
|
|
22
|
+
...tokenParams.weight,
|
|
23
|
+
spot: 0n,
|
|
24
|
+
}
|
|
25
|
+
: tokenParams.weight,
|
|
26
|
+
})),
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
exports.getStartRebalance = getStartRebalance;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Rebalance as RebalanceV5, StartRebalanceArgsPartial as StartRebalanceArgsPartialV5 } from "../types";
|
|
2
|
+
export interface Rebalance extends RebalanceV5 {
|
|
3
|
+
bidsEnabled: boolean;
|
|
4
|
+
}
|
|
5
|
+
export interface StartRebalanceArgsPartial extends StartRebalanceArgsPartialV5 {
|
|
6
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,3 +3,6 @@ export * from "./numbers";
|
|
|
3
3
|
export * from "./open-auction";
|
|
4
4
|
export * from "./start-rebalance";
|
|
5
5
|
export * from "./utils";
|
|
6
|
+
export type { Rebalance as RebalanceV4, StartRebalanceArgsPartial as StartRebalanceArgsPartialV4, } from "./4.0.0/types";
|
|
7
|
+
export type { Rebalance as RebalanceV5, StartRebalanceArgsPartial as StartRebalanceArgsPartialV5, } from "./5.0.0/types";
|
|
8
|
+
export type { Rebalance as RebalanceV6, StartRebalanceArgsPartial as StartRebalanceArgsPartialV6, } from "./6.0.0/types";
|
package/dist/open-auction.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { AuctionMetrics, FolioVersion, OpenAuctionArgs, WeightRange } from "./types";
|
|
2
2
|
import { Rebalance as Rebalance_4_0_0 } from "./4.0.0/types";
|
|
3
|
-
import { Rebalance as Rebalance_5_0_0 } from "./types";
|
|
3
|
+
import { Rebalance as Rebalance_5_0_0 } from "./5.0.0/types";
|
|
4
|
+
import { Rebalance as Rebalance_6_0_0 } from "./6.0.0/types";
|
|
4
5
|
/**
|
|
5
6
|
* Generator for the `_targetBasket` parameter
|
|
6
7
|
*
|
|
@@ -33,4 +34,4 @@ export declare const getTargetBasket: (_initialWeights: WeightRange[], _prices:
|
|
|
33
34
|
* @return OpenAuctionArgs
|
|
34
35
|
* @return AuctionMetrics
|
|
35
36
|
*/
|
|
36
|
-
export declare const getOpenAuction: (version: FolioVersion, _rebalance: Rebalance_5_0_0 | Rebalance_4_0_0, _supply: bigint, _initialSupply: bigint, _initialAssets: bigint[] | undefined, _targetBasket: bigint[] | undefined, _assets: bigint[], _decimals: bigint[], _prices: number[], _priceError: number[], _finalStageAt: number, debug?: boolean) => [OpenAuctionArgs, AuctionMetrics];
|
|
37
|
+
export declare const getOpenAuction: (version: FolioVersion, _rebalance: Rebalance_6_0_0 | Rebalance_5_0_0 | Rebalance_4_0_0, _supply: bigint, _initialSupply: bigint, _initialAssets: bigint[] | undefined, _targetBasket: bigint[] | undefined, _assets: bigint[], _decimals: bigint[], _prices: number[], _priceError: number[], _finalStageAt: number, debug?: boolean, _auctionLength?: bigint) => [OpenAuctionArgs, AuctionMetrics];
|
package/dist/open-auction.js
CHANGED
|
@@ -6,6 +6,7 @@ const numbers_1 = require("./numbers");
|
|
|
6
6
|
const types_1 = require("./types");
|
|
7
7
|
const open_auction_1 = require("./4.0.0/open-auction");
|
|
8
8
|
const open_auction_2 = require("./5.0.0/open-auction");
|
|
9
|
+
const open_auction_3 = require("./6.0.0/open-auction");
|
|
9
10
|
/**
|
|
10
11
|
* Generator for the `_targetBasket` parameter
|
|
11
12
|
*
|
|
@@ -61,20 +62,19 @@ exports.getTargetBasket = getTargetBasket;
|
|
|
61
62
|
* @return OpenAuctionArgs
|
|
62
63
|
* @return AuctionMetrics
|
|
63
64
|
*/
|
|
64
|
-
const getOpenAuction = (version, _rebalance, _supply, _initialSupply, _initialAssets = [], _targetBasket = [], _assets, _decimals, _prices, _priceError, _finalStageAt, debug) => {
|
|
65
|
+
const getOpenAuction = (version, _rebalance, _supply, _initialSupply, _initialAssets = [], _targetBasket = [], _assets, _decimals, _prices, _priceError, _finalStageAt, debug, _auctionLength) => {
|
|
65
66
|
if (debug) {
|
|
66
67
|
console.log("getOpenAuction version", version);
|
|
67
68
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
throw new Error(`unsupported version: ${version}`);
|
|
69
|
+
switch (version) {
|
|
70
|
+
case types_1.FolioVersion.V4:
|
|
71
|
+
return (0, open_auction_1.getOpenAuction)(_rebalance, _supply, _initialSupply, _initialAssets, _targetBasket, _assets, _decimals, _prices, _priceError, _finalStageAt, debug);
|
|
72
|
+
case types_1.FolioVersion.V5:
|
|
73
|
+
return (0, open_auction_2.getOpenAuction)(_rebalance, _supply, _initialSupply, _initialAssets, _targetBasket, _assets, _decimals, _prices, _priceError, _finalStageAt, debug);
|
|
74
|
+
case types_1.FolioVersion.V6:
|
|
75
|
+
return (0, open_auction_3.getOpenAuction)(_rebalance, _supply, _initialSupply, _initialAssets, _targetBasket, _assets, _decimals, _prices, _priceError, _finalStageAt, debug, _auctionLength);
|
|
76
|
+
default:
|
|
77
|
+
throw new Error(`unsupported version: ${version}`);
|
|
78
78
|
}
|
|
79
79
|
};
|
|
80
80
|
exports.getOpenAuction = getOpenAuction;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { FolioVersion } from "./types";
|
|
2
2
|
import { StartRebalanceArgsPartial as StartRebalanceArgsPartial_4_0_0 } from "./4.0.0/types";
|
|
3
|
-
import { StartRebalanceArgsPartial as StartRebalanceArgsPartial_5_0_0 } from "./types";
|
|
3
|
+
import { StartRebalanceArgsPartial as StartRebalanceArgsPartial_5_0_0 } from "./5.0.0/types";
|
|
4
|
+
import { StartRebalanceArgsPartial as StartRebalanceArgsPartial_6_0_0 } from "./6.0.0/types";
|
|
4
5
|
/**
|
|
5
6
|
* Get the arguments needed to call startRebalance
|
|
6
7
|
*
|
|
@@ -18,6 +19,6 @@ import { StartRebalanceArgsPartial as StartRebalanceArgsPartial_5_0_0 } from "./
|
|
|
18
19
|
* @param weightControl TRACKING=false, NATIVE=true
|
|
19
20
|
* @param deferWeights Whether to use the full range for weights, only possible for NATIVE DTFs
|
|
20
21
|
*
|
|
21
|
-
* @return StartRebalanceArgsPartial_5_0_0 | StartRebalanceArgsPartial_4_0_0, depending on `version` enum
|
|
22
|
+
* @return StartRebalanceArgsPartial_6_0_0 | StartRebalanceArgsPartial_5_0_0 | StartRebalanceArgsPartial_4_0_0, depending on `version` enum
|
|
22
23
|
*/
|
|
23
|
-
export declare const getStartRebalance: (version: FolioVersion, _supply: bigint, tokens: string[], _assets: bigint[], decimals: bigint[], _targetBasket: bigint[], _prices: number[], _priceError: number[], _maxAuctionSizes: number[], weightControl: boolean, deferWeights: boolean, debug?: boolean) => StartRebalanceArgsPartial_5_0_0 | StartRebalanceArgsPartial_4_0_0;
|
|
24
|
+
export declare const getStartRebalance: (version: FolioVersion, _supply: bigint, tokens: string[], _assets: bigint[], decimals: bigint[], _targetBasket: bigint[], _prices: number[], _priceError: number[], _maxAuctionSizes: number[], weightControl: boolean, deferWeights: boolean, debug?: boolean) => StartRebalanceArgsPartial_6_0_0 | StartRebalanceArgsPartial_5_0_0 | StartRebalanceArgsPartial_4_0_0;
|
package/dist/start-rebalance.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.getStartRebalance = void 0;
|
|
|
4
4
|
const types_1 = require("./types");
|
|
5
5
|
const start_rebalance_1 = require("./4.0.0/start-rebalance");
|
|
6
6
|
const start_rebalance_2 = require("./5.0.0/start-rebalance");
|
|
7
|
+
const start_rebalance_3 = require("./6.0.0/start-rebalance");
|
|
7
8
|
/**
|
|
8
9
|
* Get the arguments needed to call startRebalance
|
|
9
10
|
*
|
|
@@ -21,22 +22,21 @@ const start_rebalance_2 = require("./5.0.0/start-rebalance");
|
|
|
21
22
|
* @param weightControl TRACKING=false, NATIVE=true
|
|
22
23
|
* @param deferWeights Whether to use the full range for weights, only possible for NATIVE DTFs
|
|
23
24
|
*
|
|
24
|
-
* @return StartRebalanceArgsPartial_5_0_0 | StartRebalanceArgsPartial_4_0_0, depending on `version` enum
|
|
25
|
+
* @return StartRebalanceArgsPartial_6_0_0 | StartRebalanceArgsPartial_5_0_0 | StartRebalanceArgsPartial_4_0_0, depending on `version` enum
|
|
25
26
|
*/
|
|
26
27
|
const getStartRebalance = (version, _supply, tokens, _assets, decimals, _targetBasket, _prices, _priceError, _maxAuctionSizes, weightControl, deferWeights, debug) => {
|
|
27
28
|
if (debug) {
|
|
28
|
-
console.log("
|
|
29
|
+
console.log("getStartRebalance version", version);
|
|
29
30
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
throw new Error(`unsupported version: ${version}`);
|
|
31
|
+
switch (version) {
|
|
32
|
+
case types_1.FolioVersion.V4:
|
|
33
|
+
return (0, start_rebalance_1.getStartRebalance)(_supply, tokens, _assets, decimals, _targetBasket, _prices, _priceError, weightControl, deferWeights, debug);
|
|
34
|
+
case types_1.FolioVersion.V5:
|
|
35
|
+
return (0, start_rebalance_2.getStartRebalance)(_supply, tokens, _assets, decimals, _targetBasket, _prices, _priceError, _maxAuctionSizes, weightControl, deferWeights, debug);
|
|
36
|
+
case types_1.FolioVersion.V6:
|
|
37
|
+
return (0, start_rebalance_3.getStartRebalance)(_supply, tokens, _assets, decimals, _targetBasket, _prices, _priceError, _maxAuctionSizes, weightControl, deferWeights, debug);
|
|
38
|
+
default:
|
|
39
|
+
throw new Error(`unsupported version: ${version}`);
|
|
40
40
|
}
|
|
41
41
|
};
|
|
42
42
|
exports.getStartRebalance = getStartRebalance;
|
package/dist/types.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export declare enum FolioVersion {
|
|
2
2
|
V4 = 4,
|
|
3
|
-
V5 = 5
|
|
3
|
+
V5 = 5,
|
|
4
|
+
V6 = 6
|
|
4
5
|
}
|
|
5
6
|
export declare enum PriceControl {
|
|
6
7
|
NONE = 0,
|
|
@@ -83,4 +84,5 @@ export interface OpenAuctionArgs {
|
|
|
83
84
|
newWeights: WeightRange[];
|
|
84
85
|
newPrices: PriceRange[];
|
|
85
86
|
newLimits: RebalanceLimits;
|
|
87
|
+
auctionLength?: bigint;
|
|
86
88
|
}
|
package/dist/types.js
CHANGED
|
@@ -6,6 +6,7 @@ var FolioVersion;
|
|
|
6
6
|
(function (FolioVersion) {
|
|
7
7
|
FolioVersion[FolioVersion["V4"] = 4] = "V4";
|
|
8
8
|
FolioVersion[FolioVersion["V5"] = 5] = "V5";
|
|
9
|
+
FolioVersion[FolioVersion["V6"] = 6] = "V6";
|
|
9
10
|
})(FolioVersion || (exports.FolioVersion = FolioVersion = {}));
|
|
10
11
|
// === FOLIO DATA STRUCTURES ===
|
|
11
12
|
var PriceControl;
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
# Auction Algorithm
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The auction algorithm is a sophisticated mechanism for rebalancing portfolios (called "Folios") through a series of auction rounds. The algorithm determines what tokens to buy and sell, at what prices, and in what quantities to transition a portfolio from its current state to a target composition.
|
|
6
|
+
|
|
7
|
+
The algorithm is designed to handle any number of tokens and supports three types of rebalances, each with different approaches to manipulating weights and limits.
|
|
8
|
+
|
|
9
|
+
Initially `getStartRebalance()` is called to prepare the initial rebalance parameters. Any number of auctions (serially) are then launched via successive calls to `getOpenAuction()`.
|
|
10
|
+
|
|
11
|
+
## Key Concepts
|
|
12
|
+
|
|
13
|
+
### Core Components
|
|
14
|
+
|
|
15
|
+
1. **Weights** (`WeightRange`): Represent the amount of tokens per basket unit (BU). Expressed as D27 values with low/spot/high ranges.
|
|
16
|
+
|
|
17
|
+
- Formula: `D27{tok/BU}`
|
|
18
|
+
- Used to define the target composition of the basket
|
|
19
|
+
|
|
20
|
+
2. **Limits** (`RebalanceLimits`): Define the relationship between basket units and shares. Expressed as D18 values with low/spot/high ranges.
|
|
21
|
+
|
|
22
|
+
- Formula: `D18{BU/share}`
|
|
23
|
+
- Used to scale the basket composition to actual share holdings
|
|
24
|
+
|
|
25
|
+
3. **Prices** (`PriceRange`): Token prices in nanoUSD (D27 format) with low/high bounds for auction pricing.
|
|
26
|
+
|
|
27
|
+
- Formula: `D27{nanoUSD/tok}`
|
|
28
|
+
- Used to calculate values and determine trading ranges
|
|
29
|
+
|
|
30
|
+
4. **Target Basket**: A normalized representation (D18 format) of the portfolio's target composition by value percentage.
|
|
31
|
+
- Different calculation methods for tracking vs native rebalances
|
|
32
|
+
|
|
33
|
+
### Fundamental Relationship
|
|
34
|
+
|
|
35
|
+
The core relationship between these components is:
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
expected balance = weight × limit
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Where:
|
|
42
|
+
|
|
43
|
+
- `balance`: tokens per share `{tok/share}`
|
|
44
|
+
- `weight`: tokens per basket unit `{tok/BU}`
|
|
45
|
+
- `limit`: basket units per share `{BU/share}`
|
|
46
|
+
|
|
47
|
+
**Understanding High and Low Bounds:**
|
|
48
|
+
|
|
49
|
+
The high and low bounds define the trading boundaries for the auction:
|
|
50
|
+
|
|
51
|
+
- **Low Bounds**: Define **deficits** - what we **buy up to**
|
|
52
|
+
|
|
53
|
+
- `buyUpTo = weight.low × limit.low`
|
|
54
|
+
- If current balance < buyUpTo, the token has a deficit
|
|
55
|
+
- The auction will purchase tokens to reach this threshold
|
|
56
|
+
|
|
57
|
+
- **High Bounds**: Define **surpluses** - what we **sell down to**
|
|
58
|
+
- `sellDownTo = weight.high × limit.high`
|
|
59
|
+
- If current balance > sellDownTo, the token has a surplus
|
|
60
|
+
- The auction will sell tokens down to this threshold
|
|
61
|
+
|
|
62
|
+
This asymmetric design creates a trading range that gradually narrows in on a final spot target that is constantly updated throughout the auction in response to changing prices and variable slippage.
|
|
63
|
+
|
|
64
|
+
## Types of Rebalances
|
|
65
|
+
|
|
66
|
+
The algorithm supports three types of rebalances. First, let's understand the two simple, disjoint cases:
|
|
67
|
+
|
|
68
|
+
### 1. Tracking Rebalance (Simple Case)
|
|
69
|
+
|
|
70
|
+
- **What it does**: Adjusts portfolio scale while keeping token ratios constant
|
|
71
|
+
- **What changes**: Only limits vary
|
|
72
|
+
- **What stays fixed**: Weights remain constant (low = spot = high)
|
|
73
|
+
- **Weight Control**: `false` in `getStartRebalance()`
|
|
74
|
+
- **Target Basket**: Uses CURRENT market prices
|
|
75
|
+
- **Real-world analogy**: Like zooming in/out on a photograph - proportions stay the same, only scale changes
|
|
76
|
+
- **Example**: Portfolio maintains 60% ETH, 40% BTC ratio but adjusts total value
|
|
77
|
+
|
|
78
|
+
### 2. Native Rebalance (Simple Case)
|
|
79
|
+
|
|
80
|
+
- **What it does**: Changes portfolio composition while keeping scale constant
|
|
81
|
+
- **What changes**: Only weights vary
|
|
82
|
+
- **What stays fixed**: Limits remain at initial values
|
|
83
|
+
- **Weight Control**: `true` in `getStartRebalance()`
|
|
84
|
+
- **Target Basket**: Uses HISTORICAL prices from rebalance start
|
|
85
|
+
- **Real-world analogy**: Like rearranging furniture in a room - the room size stays the same, but contents change
|
|
86
|
+
- **Example**: Changing from 100% USDC to 50% DAI, 50% USDT
|
|
87
|
+
|
|
88
|
+
### 3. Hybrid Rebalance (Complex Case)
|
|
89
|
+
|
|
90
|
+
- **What it does**: Changes both composition AND scale simultaneously
|
|
91
|
+
- **What changes**: Both weights AND limits vary
|
|
92
|
+
- **Implementation**: Uses manually constructed `Rebalance` objects
|
|
93
|
+
- **Use Case**: Complex scenarios requiring simultaneous composition and scale changes
|
|
94
|
+
- **Real-world analogy**: Like both rearranging furniture AND changing room size
|
|
95
|
+
- **Example**: Reducing USDC from 100% to 33% while also doubling portfolio scale
|
|
96
|
+
|
|
97
|
+
## Auction Rounds
|
|
98
|
+
|
|
99
|
+
The algorithm progresses through three types of auction rounds:
|
|
100
|
+
|
|
101
|
+
### 1. Eject Round (`EJECT`)
|
|
102
|
+
|
|
103
|
+
- **Purpose**: Remove tokens with zero target weight from the portfolio
|
|
104
|
+
- **Trigger**: When `portionBeingEjected > 0` (tokens have `weight.spot == 0`)
|
|
105
|
+
- **Special Handling**:
|
|
106
|
+
- Adds 10% buffer to high limits and weights
|
|
107
|
+
- Prevents selling all surpluses upfront
|
|
108
|
+
- Allows ejected tokens to fill deficits
|
|
109
|
+
- **Target**: Either approaches `finalStageAt` or completes ejection
|
|
110
|
+
|
|
111
|
+
### 2. Progress Round (`PROGRESS`)
|
|
112
|
+
|
|
113
|
+
- **Purpose**: Move the portfolio towards the `finalStageAt` threshold
|
|
114
|
+
- **Trigger**: When progression < 99% AND relative progression < (finalStageAt - 0.02)
|
|
115
|
+
- **Target**: `initialProgression + (1 - initialProgression) × finalStageAt`
|
|
116
|
+
- **Behavior**: Gradual rebalancing to avoid market impact
|
|
117
|
+
|
|
118
|
+
### 3. Final Round (`FINAL`)
|
|
119
|
+
|
|
120
|
+
- **Purpose**: Complete the rebalance to 100%
|
|
121
|
+
- **Trigger**: When approaching or exceeding `finalStageAt` threshold
|
|
122
|
+
- **Target**: 100% completion (rebalanceTarget = 1)
|
|
123
|
+
- **Delta**: 0 (no spread between low/high bounds)
|
|
124
|
+
|
|
125
|
+
## Algorithm Flow
|
|
126
|
+
|
|
127
|
+
### Input Parameters
|
|
128
|
+
|
|
129
|
+
1. **rebalance**: Current rebalance state from `folio.getRebalance()`
|
|
130
|
+
2. **\_supply**: Total supply of shares
|
|
131
|
+
3. **\_initialFolio**: Initial token balances when rebalance started
|
|
132
|
+
4. **\_targetBasket**: Target composition by value percentage
|
|
133
|
+
5. **\_folio**: Current token balances
|
|
134
|
+
6. **\_decimals**: Token decimal places
|
|
135
|
+
7. **\_prices**: Current USD prices per whole token
|
|
136
|
+
8. **\_priceError**: Price error margins for auction pricing
|
|
137
|
+
9. **\_finalStageAt**: Progression threshold (e.g., 0.9 = 95%)
|
|
138
|
+
|
|
139
|
+
### Initial Setup with getStartRebalance
|
|
140
|
+
|
|
141
|
+
Before any auction can begin, `getStartRebalance()` prepares the initial rebalance parameters:
|
|
142
|
+
|
|
143
|
+
**For Tracking Rebalances (`weightControl = false`):**
|
|
144
|
+
|
|
145
|
+
- Weights: All three values (low/spot/high) are set identically based on target basket
|
|
146
|
+
- Limits: Calculated using price error to create asymmetric bounds
|
|
147
|
+
- `totalPortion = Σ(targetBasket[i] × priceError[i])`
|
|
148
|
+
- `low = (1 - totalPortion) × 1e18`
|
|
149
|
+
- `high = 1 / (1 - totalPortion) × 1e18`
|
|
150
|
+
- The division in `high` creates asymmetry (e.g., 10% error → 90% low, 111% high)
|
|
151
|
+
- Prices: Standard low/high based on price error
|
|
152
|
+
|
|
153
|
+
**For Native Rebalances (`weightControl = true`):**
|
|
154
|
+
|
|
155
|
+
- Weights: Vary based on price error
|
|
156
|
+
- `low = spotWeight × (1 - priceError)`
|
|
157
|
+
- `high = spotWeight / (1 - priceError)`
|
|
158
|
+
- Limits: Fixed at 1e18 for all (low/spot/high)
|
|
159
|
+
- Prices: Same calculation as tracking
|
|
160
|
+
|
|
161
|
+
### Processing Steps
|
|
162
|
+
|
|
163
|
+
1. **Calculate Current State**
|
|
164
|
+
|
|
165
|
+
- Convert all values to common decimal format
|
|
166
|
+
- Calculate share value and basket unit value
|
|
167
|
+
- Determine ideal spot limit: `shareValue / buValue`
|
|
168
|
+
|
|
169
|
+
2. **Calculate Progression**
|
|
170
|
+
|
|
171
|
+
- **Absolute Progression**: Percentage of balances in correct position (0-100%)
|
|
172
|
+
- **Relative Progression**: Progress from initial to target
|
|
173
|
+
- Formula: `(current - initial) / (1 - initial)`
|
|
174
|
+
|
|
175
|
+
3. **Determine Auction Round**
|
|
176
|
+
|
|
177
|
+
- Check for ejections first
|
|
178
|
+
- Then check progression thresholds
|
|
179
|
+
- Default to FINAL if near completion
|
|
180
|
+
|
|
181
|
+
4. **Calculate New Limits**
|
|
182
|
+
|
|
183
|
+
- Base calculation: `spotLimit × (1 ± delta)`
|
|
184
|
+
- Constrained by initial rebalance limits
|
|
185
|
+
- Delta derived from target progression
|
|
186
|
+
|
|
187
|
+
5. **Calculate New Weights**
|
|
188
|
+
|
|
189
|
+
- Ideal weight: `shareValue × targetBasket / actualLimits.spot / price`
|
|
190
|
+
- Adjusted for delta while avoiding double-counting uncertainty
|
|
191
|
+
- Formula: `idealWeight × (1 ± delta) / (limitRatio)`
|
|
192
|
+
- The `limitRatio` division prevents propagating uncertainty twice
|
|
193
|
+
- Constrained by initial weight ranges
|
|
194
|
+
|
|
195
|
+
6. **Calculate New Prices**
|
|
196
|
+
|
|
197
|
+
- Based on current prices ± price error
|
|
198
|
+
- Constrained by initial price ranges
|
|
199
|
+
- Only adjusted if `priceControl != NONE`
|
|
200
|
+
|
|
201
|
+
7. **Filter Tradeable Tokens**
|
|
202
|
+
- Include only tokens with surpluses or deficits
|
|
203
|
+
- Exclude tokens not in rebalance
|
|
204
|
+
- Minimum trade value: $1
|
|
205
|
+
|
|
206
|
+
### Output
|
|
207
|
+
|
|
208
|
+
The algorithm returns:
|
|
209
|
+
|
|
210
|
+
1. **OpenAuctionArgs**: Parameters for calling `folio.openAuction()`
|
|
211
|
+
|
|
212
|
+
- rebalanceNonce
|
|
213
|
+
- tokens (filtered list)
|
|
214
|
+
- newWeights
|
|
215
|
+
- newPrices
|
|
216
|
+
- newLimits
|
|
217
|
+
|
|
218
|
+
2. **AuctionMetrics**: Useful metrics for monitoring
|
|
219
|
+
- round type
|
|
220
|
+
- progression values (initial, absolute, relative)
|
|
221
|
+
- target values
|
|
222
|
+
- auction size in USD
|
|
223
|
+
- surplus/deficit token lists
|
|
224
|
+
|
|
225
|
+
## Example Walkthroughs
|
|
226
|
+
|
|
227
|
+
### Tracking Rebalance: 100% USDC → 50% DAI, 50% USDT
|
|
228
|
+
|
|
229
|
+
This example shows how limits change while weights stay constant throughout.
|
|
230
|
+
|
|
231
|
+
**Initial Setup:**
|
|
232
|
+
|
|
233
|
+
- Starting Folio: [1 USDC, 0 DAI, 0 USDT] per share
|
|
234
|
+
- Target: [0%, 50%, 50%] by value (using current prices)
|
|
235
|
+
- Weights (constant throughout): USDC=0, DAI=5e26, USDT=5e14 (in D27 format)
|
|
236
|
+
- Price Error: 10% for each token
|
|
237
|
+
- Initial Limits calculation:
|
|
238
|
+
- totalPortion = (0×0.1) + (0.5×0.1) + (0.5×0.1) = 0.1
|
|
239
|
+
- low = (1 - 0.1) × 1e18 = 9e17
|
|
240
|
+
- high = 1/(1 - 0.1) × 1e18 = 1.111...e18
|
|
241
|
+
- Note the asymmetry: high uses division, not simple addition
|
|
242
|
+
|
|
243
|
+
**Scenario 1 - Ejection Round:**
|
|
244
|
+
|
|
245
|
+
- Current Folio: [1 USDC, 0 DAI, 0 USDT] (still at start)
|
|
246
|
+
- Current Limits: {low: 9.5e17, spot: 1e18, high: 1.11e18}
|
|
247
|
+
- USDC marked for ejection (weight = 0)
|
|
248
|
+
- Progression: 0%
|
|
249
|
+
- Target: 95% (approaching finalStageAt)
|
|
250
|
+
- New Limits: {low: 9.5e17, spot: 1e18, high: 1.11e18} (adjusted for delta)
|
|
251
|
+
- Action: Sell USDC using limit-based pricing, buy DAI/USDT
|
|
252
|
+
|
|
253
|
+
**Scenario 2 - Final Round:**
|
|
254
|
+
|
|
255
|
+
- Current Folio: [0.05 USDC, 0.475 DAI, 0.475 USDT] by value
|
|
256
|
+
- Low limit of 0.9 constrained the first auction
|
|
257
|
+
- Progression: 95% (reached finalStageAt threshold)
|
|
258
|
+
- Round: FINAL (skipped PROGRESS since we hit finalStageAt)
|
|
259
|
+
- New Limits: {low: 1e18, spot: 1e18, high: 1e18} (delta = 0)
|
|
260
|
+
- Action: Sell remaining USDC, reach exact 50/50 DAI/USDT split
|
|
261
|
+
- Final Result: [0 USDC, 0.5 DAI, 0.5 USDT] by value
|
|
262
|
+
|
|
263
|
+
**Key Insight**: Throughout this rebalance, weights never changed. Only limits varied to achieve the target composition through the relationship `balance = weight × limit`.
|
|
264
|
+
|
|
265
|
+
### Native Rebalance: 100% USDC → 50% DAI, 50% USDT
|
|
266
|
+
|
|
267
|
+
This example shows how weights change while limits stay constant.
|
|
268
|
+
|
|
269
|
+
**Initial Setup:**
|
|
270
|
+
|
|
271
|
+
- Starting Folio: [1 USDC, 0 DAI, 0 USDT] per share
|
|
272
|
+
- Target Basket: [0%, 50%, 50%] by value (using historical prices)
|
|
273
|
+
- Limits (constant throughout): {low: 1e18, spot: 1e18, high: 1e18}
|
|
274
|
+
- Price Error: 10% for each token
|
|
275
|
+
- Initial Weights calculation:
|
|
276
|
+
- USDC: {low: 0, spot: 0, high: 0} (marked for ejection)
|
|
277
|
+
- DAI: {low: 4.5e26, spot: 5e26, high: 5.55e26} (D27 format)
|
|
278
|
+
- USDT: {low: 4.5e14, spot: 5e14, high: 5.55e14} (D27 format)
|
|
279
|
+
|
|
280
|
+
**Scenario 1 - Ejection Round:**
|
|
281
|
+
|
|
282
|
+
- Current Folio: [1 USDC, 0 DAI, 0 USDT] (still at start)
|
|
283
|
+
- Current Weights: USDC=0, DAI and USDT have ranges as above
|
|
284
|
+
- Progression: 0%
|
|
285
|
+
- Target: 95% (approaching finalStageAt)
|
|
286
|
+
- New Weights (with 5% delta applied):
|
|
287
|
+
- USDC: remains at 0
|
|
288
|
+
- DAI/USDT: adjusted ranges to enable trading
|
|
289
|
+
- Action: Sell USDC using weight-based pricing, buy DAI/USDT
|
|
290
|
+
|
|
291
|
+
**Scenario 2 - Final Round:**
|
|
292
|
+
|
|
293
|
+
- Current Folio: [0.05 USDC, 0.475 DAI, 0.475 USDT] by value
|
|
294
|
+
- Progression: 95% (reached finalStageAt threshold)
|
|
295
|
+
- Round: FINAL (skipped PROGRESS since we hit finalStageAt)
|
|
296
|
+
- New Weights (delta = 0):
|
|
297
|
+
- USDC: {low: 0, spot: 0, high: 0}
|
|
298
|
+
- DAI: {low: 5e26, spot: 5e26, high: 5e26} (converged to spot)
|
|
299
|
+
- USDT: {low: 5e14, spot: 5e14, high: 5e14} (converged to spot)
|
|
300
|
+
- Action: Sell remaining USDC, reach exact 50/50 DAI/USDT split
|
|
301
|
+
- Final Result: [0 USDC, 0.5 DAI, 0.5 USDT] by value
|
|
302
|
+
|
|
303
|
+
**Key Insight**: Throughout this rebalance, limits never changed from 1e18. Only weights varied to achieve the target composition.
|
|
304
|
+
|
|
305
|
+
## Price Control
|
|
306
|
+
|
|
307
|
+
The `PriceControl` enum affects price adjustments:
|
|
308
|
+
|
|
309
|
+
- **NONE**: No price manipulation allowed; prices remain at initial ranges
|
|
310
|
+
- **PARTIAL**: Prices can be adjusted within bounds based on current market prices and price error
|
|
311
|
+
|
|
312
|
+
## Key Formulas
|
|
313
|
+
|
|
314
|
+
### Target Basket Calculation
|
|
315
|
+
|
|
316
|
+
```javascript
|
|
317
|
+
targetBasket[i] = (initialWeight[i] × price[i]) / totalValue
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Progression Calculation
|
|
321
|
+
|
|
322
|
+
```javascript
|
|
323
|
+
absoluteProgression = Σ(min(actual[i], expected[i]) × price[i]) / shareValue
|
|
324
|
+
relativeProgression = (absolute - initial) / (1 - initial)
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### Delta Application and Uncertainty Propagation
|
|
328
|
+
|
|
329
|
+
The delta represents the uncertainty or spread in the rebalancing process. It's crucial to avoid double-counting this uncertainty:
|
|
330
|
+
|
|
331
|
+
- **For Limits**: `limit × (1 ± delta)`
|
|
332
|
+
|
|
333
|
+
- Uncertainty is directly applied to limits
|
|
334
|
+
|
|
335
|
+
- **For Weights**: `weight × (1 ± delta) / (limitRatio)`
|
|
336
|
+
- Where `limitRatio = actualLimit / spotLimit`
|
|
337
|
+
- The division by `limitRatio` accounts for uncertainty already propagated to limits
|
|
338
|
+
- This prevents double-counting the delta uncertainty
|
|
339
|
+
- Since `balance = weight × limit`, if limits already contain uncertainty, weights must be adjusted to avoid compounding it
|
|
340
|
+
|
|
341
|
+
## Error Handling
|
|
342
|
+
|
|
343
|
+
The algorithm includes several safety checks:
|
|
344
|
+
|
|
345
|
+
1. Spot prices must remain within initial price bounds
|
|
346
|
+
2. Progression should not go backwards (except for rounding)
|
|
347
|
+
3. BU value and share value should not differ by more than 10x
|
|
348
|
+
4. All array lengths must match
|
|
349
|
+
5. Minimum $1 trade value per token
|
|
350
|
+
|
|
351
|
+
## Usage Notes
|
|
352
|
+
|
|
353
|
+
1. **AUCTION_LAUNCHER** role uses `getOpenAuction()` results with `folio.openAuction()`
|
|
354
|
+
2. Non-launchers should use `folio.openAuctionUnrestricted()`
|
|
355
|
+
3. The algorithm is stateless - each call recalculates from current state
|
|
356
|
+
4. Prices should be passed differently for tracking vs native rebalances
|
|
357
|
+
5. The 10% buffer in ejection rounds prevents premature surplus depletion
|
package/package.json
CHANGED
|
@@ -1,23 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reserve-protocol/dtf-rebalance-lib",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.1",
|
|
4
4
|
"description": "Rebalancing library for DTFs in typescript",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=20"
|
|
10
|
+
},
|
|
8
11
|
"files": [
|
|
9
12
|
"dist",
|
|
13
|
+
"docs",
|
|
10
14
|
"LICENSE.md",
|
|
11
15
|
"README.md"
|
|
12
16
|
],
|
|
17
|
+
"workspaces": [
|
|
18
|
+
"packages/*"
|
|
19
|
+
],
|
|
13
20
|
"scripts": {
|
|
14
21
|
"clean": "rm -rf dist",
|
|
15
22
|
"compile": "hardhat compile",
|
|
23
|
+
"compile:dtf-artifacts": "FOUNDRY_PROFILE=reserve-index-dtf forge build --skip script --skip test",
|
|
16
24
|
"build": "npm run clean && tsc --project tsconfig.build.json",
|
|
25
|
+
"build:internal-tools": "npm run build -w @reserve-protocol/dtf-rebalance-tools",
|
|
26
|
+
"build:tools": "npm run build:internal-tools",
|
|
17
27
|
"prepublishOnly": "npm run build",
|
|
18
28
|
"test": "npm run test:unit && npm run test:e2e",
|
|
19
29
|
"test:unit": "node --test --require ts-node/register test/**/unit.test.ts",
|
|
20
|
-
"
|
|
30
|
+
"pretest:e2e": "npm run compile:dtf-artifacts",
|
|
31
|
+
"test:e2e": "hardhat test packages/tools/test/*.test.ts --bail"
|
|
21
32
|
},
|
|
22
33
|
"publishConfig": {
|
|
23
34
|
"access": "public"
|
|
@@ -43,8 +54,8 @@
|
|
|
43
54
|
"devDependencies": {
|
|
44
55
|
"@nomicfoundation/hardhat-toolbox": "^5.0.0",
|
|
45
56
|
"@openzeppelin/contracts": "^5.3.0",
|
|
46
|
-
"@reserve-protocol/reserve-index-dtf": "github:reserve-protocol/reserve-index-dtf#
|
|
47
|
-
"@types/node": "^
|
|
57
|
+
"@reserve-protocol/reserve-index-dtf": "github:reserve-protocol/reserve-index-dtf#e3b66635f13f3059e1762255882d4fc21c4e03df",
|
|
58
|
+
"@types/node": "^24.0.0",
|
|
48
59
|
"dotenv": "^16.5.0",
|
|
49
60
|
"hardhat": "^2.24.1",
|
|
50
61
|
"prettier": "^3.5.3",
|