impermax-sdk 2.1.261 → 2.1.263
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/lib/utils/position/genericPosition.d.ts +134 -0
- package/lib/utils/position/genericPosition.js +468 -0
- package/lib/utils/position/uniswapV2/index.d.ts +8 -104
- package/lib/utils/position/uniswapV2/index.js +11 -445
- package/lib/utils/position/uniswapV3/index.d.ts +19 -111
- package/lib/utils/position/uniswapV3/index.js +23 -438
- package/package.json +1 -1
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import Position from "./interface";
|
|
2
|
+
export interface GenericPositionParams {
|
|
3
|
+
liquidity: number;
|
|
4
|
+
debtX: number;
|
|
5
|
+
debtY: number;
|
|
6
|
+
marketPrice: number;
|
|
7
|
+
oraclePrice: number;
|
|
8
|
+
safetyMargin: number;
|
|
9
|
+
liquidationPenalty: number;
|
|
10
|
+
availableToBorrowX?: number;
|
|
11
|
+
availableToBorrowY?: number;
|
|
12
|
+
lockStateChange?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export declare abstract class GenericPosition implements Position {
|
|
15
|
+
lockStateChange: boolean;
|
|
16
|
+
state: number;
|
|
17
|
+
/**
|
|
18
|
+
* NOTICE all values inside this class are normalized (no decimals or mantissa to take into account)
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* TODO: Cache for initialRealX, intialCollateralValue, etc...
|
|
22
|
+
*/
|
|
23
|
+
liquidity: number;
|
|
24
|
+
debtX: number;
|
|
25
|
+
debtY: number;
|
|
26
|
+
initialLiquidity: number;
|
|
27
|
+
initialDebtX: number;
|
|
28
|
+
initialDebtY: number;
|
|
29
|
+
marketPrice: number;
|
|
30
|
+
oraclePrice: number;
|
|
31
|
+
safetyMargin: number;
|
|
32
|
+
liquidationPenalty: number;
|
|
33
|
+
availableToBorrowX: number;
|
|
34
|
+
availableToBorrowY: number;
|
|
35
|
+
constructor(params: GenericPositionParams);
|
|
36
|
+
protected checkLock(): void;
|
|
37
|
+
/**
|
|
38
|
+
* protected SETTERS
|
|
39
|
+
*/
|
|
40
|
+
protected abstract setLiquidity(liquidity: number): any;
|
|
41
|
+
protected setRealXRealY(amountX: number, amountY: number): void;
|
|
42
|
+
protected setRealX(amountX: number): void;
|
|
43
|
+
protected setRealY(amountY: number): void;
|
|
44
|
+
protected setDebtX(_debtX: number): void;
|
|
45
|
+
protected setDebtY(_debtY: number): void;
|
|
46
|
+
/**
|
|
47
|
+
* protected GETTERS
|
|
48
|
+
*/
|
|
49
|
+
protected abstract getRealXGivenLiquidityAndPrice(liquidity: number, price: number): any;
|
|
50
|
+
protected abstract getRealYGivenLiquidityAndPrice(liquidity: number, price: number): any;
|
|
51
|
+
protected getRealXGivenLiquidity(liquidity: number): any;
|
|
52
|
+
protected getRealYGivenLiquidity(liquidity: number): any;
|
|
53
|
+
protected getRealXGivenPrice(price: number): any;
|
|
54
|
+
protected getRealYGivenPrice(price: number): any;
|
|
55
|
+
protected getRealX(): any;
|
|
56
|
+
protected getRealY(): any;
|
|
57
|
+
protected getInitialRealX(): any;
|
|
58
|
+
protected getInitialRealY(): any;
|
|
59
|
+
protected getValueGivenPriceAndAmounts(price: number, amountX: number, amountY: number): number;
|
|
60
|
+
protected getValueGivenAmounts(amountX: number, amountY: number): number;
|
|
61
|
+
protected getAmountXGivenValue(value: number): number;
|
|
62
|
+
protected getAmountYGivenValue(value: number): number;
|
|
63
|
+
protected getInitialDebtValue(): number;
|
|
64
|
+
protected getDebtValueGivenPrice(price: number): number;
|
|
65
|
+
protected getInitialCollateralValue(): number;
|
|
66
|
+
protected getCollateralValueGivenPrice(price: number): number;
|
|
67
|
+
protected getCollateralValueGivenPriceAndDeltaLeverage(price: number, deltaX: number, deltaY: number): number;
|
|
68
|
+
protected getUsableCollateralValueGivenPrice(price: number): number;
|
|
69
|
+
protected getUsableCollateralValueGivenPriceAndDeltaLeverage(price: number, deltaX: number, deltaY: number): number;
|
|
70
|
+
protected getInitialEquityValue(): number;
|
|
71
|
+
protected getEquityValueGivenPrice(price: number): number;
|
|
72
|
+
protected getDebtValue(): number;
|
|
73
|
+
protected getCollateralValue(): number;
|
|
74
|
+
protected getEquityValue(): number;
|
|
75
|
+
protected hasEnoughLiquidityGivenPriceAndDebt(price: number, debtX: number, debtY: number): boolean;
|
|
76
|
+
protected hasEnoughUsableLiquidityGivenPriceAndDebt(price: number, debtX: number, debtY: number): boolean;
|
|
77
|
+
protected hasEnoughUsableLiquidityGivenPriceAndDeltaLeverage(price: number, deltaX: number, deltaY: number): boolean;
|
|
78
|
+
protected isLiquidatableGivenPriceAndDebt(price: number, debtX: number, debtY: number): boolean;
|
|
79
|
+
protected isLiquidatableGivenPriceAndDeltaLeverage(price: number, deltaX: number, deltaY: number): boolean;
|
|
80
|
+
protected isLiquidatableGivenDebt(debtX: number, debtY: number): boolean;
|
|
81
|
+
protected isLiquidatableGivenDeltaLeverage(deltaX: number, deltaY: number): boolean;
|
|
82
|
+
protected isLiquidatableGivenPrice(price: number): boolean;
|
|
83
|
+
protected isUnderwaterGivenPrice(price: number): boolean;
|
|
84
|
+
protected getLiquidationPriceInRange(lowPrice: number, highPrice: number, lowIsLiquidatable: boolean, highIsLiquidatable: boolean): number | undefined;
|
|
85
|
+
protected getMaxDeltaDebtInRange(lowDeltaX: number, lowDeltaY: number, highDeltaX: number, highDeltaY: number): [number, number];
|
|
86
|
+
protected getMaxDeltaLeverageInRange(lowDeltaX: number, lowDeltaY: number, highDeltaX: number, highDeltaY: number): [number, number];
|
|
87
|
+
protected getMinLeverageInRange(lowLeverage: number, highLeverage: number): number;
|
|
88
|
+
/**
|
|
89
|
+
* PUBLIC SETTERS
|
|
90
|
+
*/
|
|
91
|
+
depositX(amount: number): void;
|
|
92
|
+
depositY(amount: number): void;
|
|
93
|
+
withdrawX(amount: number): void;
|
|
94
|
+
withdrawY(amount: number): void;
|
|
95
|
+
borrowX(amount: number): void;
|
|
96
|
+
borrowY(amount: number): void;
|
|
97
|
+
repayX(amount: number): void;
|
|
98
|
+
repayY(amount: number): void;
|
|
99
|
+
/**
|
|
100
|
+
* PUBLIC GETTERS
|
|
101
|
+
*/
|
|
102
|
+
getInitialDepositedX(): any;
|
|
103
|
+
getInitialDepositedY(): any;
|
|
104
|
+
getDepositedX(): any;
|
|
105
|
+
getDepositedY(): any;
|
|
106
|
+
getNetX(): number;
|
|
107
|
+
getNetY(): number;
|
|
108
|
+
isLiquidatable(): boolean;
|
|
109
|
+
isUnderwater(): boolean;
|
|
110
|
+
getLiquidationRange(): {
|
|
111
|
+
priceA: number;
|
|
112
|
+
priceB: number;
|
|
113
|
+
} | undefined;
|
|
114
|
+
getInitialLeverage(): number;
|
|
115
|
+
getLeverage(): number;
|
|
116
|
+
abstract getOptimalLiquidity(amountX: number, amountY: number): any;
|
|
117
|
+
getOptimalWithdraw(amountX: number, amountY: number, withdrawAtLeast?: boolean): {
|
|
118
|
+
amountX: number;
|
|
119
|
+
amountY: number;
|
|
120
|
+
};
|
|
121
|
+
getOptimalWithdrawForDeleverage(targetLeverage: number): {
|
|
122
|
+
amountX: number;
|
|
123
|
+
amountY: number;
|
|
124
|
+
} | null;
|
|
125
|
+
getMaxLeverage(): number;
|
|
126
|
+
getMinLeverage(): number;
|
|
127
|
+
getMaxWithdrawable(): {
|
|
128
|
+
amountX: number;
|
|
129
|
+
amountY: number;
|
|
130
|
+
};
|
|
131
|
+
getMaxBorrowableX(): number;
|
|
132
|
+
getMaxBorrowableY(): number;
|
|
133
|
+
}
|
|
134
|
+
export default GenericPosition;
|
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GenericPosition = void 0;
|
|
4
|
+
const LOWEST_PRICE = 1 / 1e18;
|
|
5
|
+
const HIGHEST_PRICE = 1e18;
|
|
6
|
+
class GenericPosition {
|
|
7
|
+
constructor(params) {
|
|
8
|
+
this.liquidity = params.liquidity;
|
|
9
|
+
this.debtX = params.debtX;
|
|
10
|
+
this.debtY = params.debtY;
|
|
11
|
+
this.initialLiquidity = params.liquidity;
|
|
12
|
+
this.initialDebtX = params.debtX;
|
|
13
|
+
this.initialDebtY = params.debtY;
|
|
14
|
+
this.marketPrice = params.marketPrice;
|
|
15
|
+
this.oraclePrice = params.oraclePrice;
|
|
16
|
+
this.safetyMargin = params.safetyMargin;
|
|
17
|
+
this.liquidationPenalty = params.liquidationPenalty;
|
|
18
|
+
this.availableToBorrowX = params.availableToBorrowX ?? 0;
|
|
19
|
+
this.availableToBorrowY = params.availableToBorrowY ?? 0;
|
|
20
|
+
this.lockStateChange = params.lockStateChange ?? true;
|
|
21
|
+
this.state = 0;
|
|
22
|
+
}
|
|
23
|
+
checkLock() {
|
|
24
|
+
if (this.lockStateChange)
|
|
25
|
+
throw Error("Can't change state of original position object");
|
|
26
|
+
else
|
|
27
|
+
this.state++;
|
|
28
|
+
}
|
|
29
|
+
setRealXRealY(amountX, amountY) {
|
|
30
|
+
if (Number.isNaN(amountX))
|
|
31
|
+
return;
|
|
32
|
+
if (Number.isNaN(amountY))
|
|
33
|
+
return;
|
|
34
|
+
this.checkLock();
|
|
35
|
+
const { liquidity } = this.getOptimalLiquidity(amountX, amountY);
|
|
36
|
+
this.setLiquidity(liquidity);
|
|
37
|
+
}
|
|
38
|
+
setRealX(amountX) {
|
|
39
|
+
if (Number.isNaN(amountX))
|
|
40
|
+
return;
|
|
41
|
+
this.checkLock();
|
|
42
|
+
const { liquidity } = this.getOptimalLiquidity(amountX, Infinity);
|
|
43
|
+
this.setLiquidity(liquidity);
|
|
44
|
+
}
|
|
45
|
+
setRealY(amountY) {
|
|
46
|
+
if (Number.isNaN(amountY))
|
|
47
|
+
return;
|
|
48
|
+
this.checkLock();
|
|
49
|
+
const { liquidity } = this.getOptimalLiquidity(Infinity, amountY);
|
|
50
|
+
this.setLiquidity(liquidity);
|
|
51
|
+
}
|
|
52
|
+
setDebtX(_debtX) {
|
|
53
|
+
if (Number.isNaN(_debtX))
|
|
54
|
+
return;
|
|
55
|
+
this.checkLock();
|
|
56
|
+
this.debtX = _debtX;
|
|
57
|
+
}
|
|
58
|
+
setDebtY(_debtY) {
|
|
59
|
+
if (Number.isNaN(_debtY))
|
|
60
|
+
return;
|
|
61
|
+
this.checkLock();
|
|
62
|
+
this.debtY = _debtY;
|
|
63
|
+
}
|
|
64
|
+
getRealXGivenLiquidity(liquidity) {
|
|
65
|
+
return this.getRealXGivenLiquidityAndPrice(liquidity, this.marketPrice);
|
|
66
|
+
}
|
|
67
|
+
getRealYGivenLiquidity(liquidity) {
|
|
68
|
+
return this.getRealYGivenLiquidityAndPrice(liquidity, this.marketPrice);
|
|
69
|
+
}
|
|
70
|
+
getRealXGivenPrice(price) {
|
|
71
|
+
return this.getRealXGivenLiquidityAndPrice(this.liquidity, price);
|
|
72
|
+
}
|
|
73
|
+
getRealYGivenPrice(price) {
|
|
74
|
+
return this.getRealYGivenLiquidityAndPrice(this.liquidity, price);
|
|
75
|
+
}
|
|
76
|
+
getRealX() {
|
|
77
|
+
return this.getRealXGivenLiquidityAndPrice(this.liquidity, this.marketPrice);
|
|
78
|
+
}
|
|
79
|
+
getRealY() {
|
|
80
|
+
return this.getRealYGivenLiquidityAndPrice(this.liquidity, this.marketPrice);
|
|
81
|
+
}
|
|
82
|
+
getInitialRealX() {
|
|
83
|
+
return this.getRealXGivenLiquidityAndPrice(this.initialLiquidity, this.marketPrice);
|
|
84
|
+
}
|
|
85
|
+
getInitialRealY() {
|
|
86
|
+
return this.getRealYGivenLiquidityAndPrice(this.initialLiquidity, this.marketPrice);
|
|
87
|
+
}
|
|
88
|
+
getValueGivenPriceAndAmounts(price, amountX, amountY) {
|
|
89
|
+
return amountX * price + amountY;
|
|
90
|
+
}
|
|
91
|
+
getValueGivenAmounts(amountX, amountY) {
|
|
92
|
+
return amountX * this.marketPrice + amountY;
|
|
93
|
+
}
|
|
94
|
+
getAmountXGivenValue(value) {
|
|
95
|
+
return value / this.marketPrice;
|
|
96
|
+
}
|
|
97
|
+
getAmountYGivenValue(value) {
|
|
98
|
+
return value;
|
|
99
|
+
}
|
|
100
|
+
getInitialDebtValue() {
|
|
101
|
+
return this.getValueGivenAmounts(this.initialDebtX, this.initialDebtY);
|
|
102
|
+
}
|
|
103
|
+
getDebtValueGivenPrice(price) {
|
|
104
|
+
return this.getValueGivenPriceAndAmounts(price, this.debtX, this.debtY);
|
|
105
|
+
}
|
|
106
|
+
getInitialCollateralValue() {
|
|
107
|
+
return this.getValueGivenAmounts(this.getInitialRealX(), this.getInitialRealY());
|
|
108
|
+
}
|
|
109
|
+
getCollateralValueGivenPrice(price) {
|
|
110
|
+
return this.getValueGivenPriceAndAmounts(price, this.getRealXGivenPrice(price), this.getRealYGivenPrice(price));
|
|
111
|
+
}
|
|
112
|
+
getCollateralValueGivenPriceAndDeltaLeverage(price, deltaX, deltaY) {
|
|
113
|
+
const { liquidity } = this.getOptimalLiquidity(deltaX, deltaY);
|
|
114
|
+
return this.getValueGivenPriceAndAmounts(price, this.getRealXGivenLiquidityAndPrice(this.liquidity + liquidity, price), this.getRealYGivenLiquidityAndPrice(this.liquidity + liquidity, price));
|
|
115
|
+
}
|
|
116
|
+
getUsableCollateralValueGivenPrice(price) {
|
|
117
|
+
return this.getCollateralValueGivenPrice(price);
|
|
118
|
+
}
|
|
119
|
+
getUsableCollateralValueGivenPriceAndDeltaLeverage(price, deltaX, deltaY) {
|
|
120
|
+
return this.getCollateralValueGivenPriceAndDeltaLeverage(price, deltaX, deltaY);
|
|
121
|
+
}
|
|
122
|
+
getInitialEquityValue() {
|
|
123
|
+
return this.getInitialCollateralValue() - this.getInitialDebtValue();
|
|
124
|
+
}
|
|
125
|
+
getEquityValueGivenPrice(price) {
|
|
126
|
+
return this.getCollateralValueGivenPrice(price) - this.getDebtValueGivenPrice(price);
|
|
127
|
+
}
|
|
128
|
+
getDebtValue() {
|
|
129
|
+
return this.getDebtValueGivenPrice(this.marketPrice);
|
|
130
|
+
}
|
|
131
|
+
getCollateralValue() {
|
|
132
|
+
return this.getCollateralValueGivenPrice(this.marketPrice);
|
|
133
|
+
}
|
|
134
|
+
getEquityValue() {
|
|
135
|
+
return this.getEquityValueGivenPrice(this.marketPrice);
|
|
136
|
+
}
|
|
137
|
+
hasEnoughLiquidityGivenPriceAndDebt(price, debtX, debtY) {
|
|
138
|
+
const debtValue = this.getValueGivenPriceAndAmounts(price, debtX, debtY);
|
|
139
|
+
const collateralValue = this.getCollateralValueGivenPrice(price);
|
|
140
|
+
const collateralNeeded = debtValue * this.liquidationPenalty;
|
|
141
|
+
return collateralValue >= collateralNeeded;
|
|
142
|
+
}
|
|
143
|
+
hasEnoughUsableLiquidityGivenPriceAndDebt(price, debtX, debtY) {
|
|
144
|
+
const debtValue = this.getValueGivenPriceAndAmounts(price, debtX, debtY);
|
|
145
|
+
const collateralValue = this.getUsableCollateralValueGivenPrice(price);
|
|
146
|
+
const collateralNeeded = debtValue * this.liquidationPenalty;
|
|
147
|
+
return collateralValue >= collateralNeeded;
|
|
148
|
+
}
|
|
149
|
+
hasEnoughUsableLiquidityGivenPriceAndDeltaLeverage(price, deltaX, deltaY) {
|
|
150
|
+
const debtValue = this.getDebtValueGivenPrice(price) + this.getValueGivenPriceAndAmounts(price, deltaX, deltaY);
|
|
151
|
+
const collateralValue = this.getUsableCollateralValueGivenPriceAndDeltaLeverage(price, deltaX, deltaY);
|
|
152
|
+
const collateralNeeded = debtValue * this.liquidationPenalty;
|
|
153
|
+
return collateralValue >= collateralNeeded;
|
|
154
|
+
}
|
|
155
|
+
isLiquidatableGivenPriceAndDebt(price, debtX, debtY) {
|
|
156
|
+
return !this.hasEnoughUsableLiquidityGivenPriceAndDebt(price / this.safetyMargin, debtX, debtY)
|
|
157
|
+
|| !this.hasEnoughUsableLiquidityGivenPriceAndDebt(price * this.safetyMargin, debtX, debtY);
|
|
158
|
+
}
|
|
159
|
+
isLiquidatableGivenPriceAndDeltaLeverage(price, deltaX, deltaY) {
|
|
160
|
+
return !this.hasEnoughUsableLiquidityGivenPriceAndDeltaLeverage(price / this.safetyMargin, deltaX, deltaY)
|
|
161
|
+
|| !this.hasEnoughUsableLiquidityGivenPriceAndDeltaLeverage(price * this.safetyMargin, deltaX, deltaY);
|
|
162
|
+
}
|
|
163
|
+
isLiquidatableGivenDebt(debtX, debtY) {
|
|
164
|
+
return this.isLiquidatableGivenPriceAndDebt(this.marketPrice, debtX, debtY);
|
|
165
|
+
}
|
|
166
|
+
isLiquidatableGivenDeltaLeverage(deltaX, deltaY) {
|
|
167
|
+
return this.isLiquidatableGivenPriceAndDeltaLeverage(this.marketPrice, deltaX, deltaY);
|
|
168
|
+
}
|
|
169
|
+
isLiquidatableGivenPrice(price) {
|
|
170
|
+
return this.isLiquidatableGivenPriceAndDebt(price, this.debtX, this.debtY);
|
|
171
|
+
}
|
|
172
|
+
isUnderwaterGivenPrice(price) {
|
|
173
|
+
return !this.hasEnoughLiquidityGivenPriceAndDebt(price, this.debtX, this.debtY);
|
|
174
|
+
}
|
|
175
|
+
getLiquidationPriceInRange(lowPrice, highPrice, lowIsLiquidatable, highIsLiquidatable) {
|
|
176
|
+
if (lowIsLiquidatable == highIsLiquidatable)
|
|
177
|
+
return undefined;
|
|
178
|
+
const avgPrice = Math.sqrt(lowPrice * highPrice);
|
|
179
|
+
if (lowPrice / highPrice > 0.9999)
|
|
180
|
+
return avgPrice;
|
|
181
|
+
const avgIsLiquidatable = this.isLiquidatableGivenPrice(avgPrice);
|
|
182
|
+
return this.getLiquidationPriceInRange(lowPrice, avgPrice, lowIsLiquidatable, avgIsLiquidatable)
|
|
183
|
+
?? this.getLiquidationPriceInRange(avgPrice, highPrice, avgIsLiquidatable, highIsLiquidatable);
|
|
184
|
+
}
|
|
185
|
+
getMaxDeltaDebtInRange(lowDeltaX, lowDeltaY, highDeltaX, highDeltaY) {
|
|
186
|
+
if (Number.isNaN(lowDeltaX) || Number.isNaN(lowDeltaY) || Number.isNaN(highDeltaX) || Number.isNaN(highDeltaY))
|
|
187
|
+
return [0, 0];
|
|
188
|
+
if (highDeltaX == 0 && highDeltaY == 0)
|
|
189
|
+
return [0, 0];
|
|
190
|
+
if (Math.abs(highDeltaX / lowDeltaX) < 1.001 || Math.abs(highDeltaY / lowDeltaY) < 1.001)
|
|
191
|
+
return [lowDeltaX, lowDeltaY];
|
|
192
|
+
const avgDeltaX = (lowDeltaX + highDeltaX) / 2;
|
|
193
|
+
const avgDeltaY = (lowDeltaY + highDeltaY) / 2;
|
|
194
|
+
if (this.isLiquidatableGivenDebt(this.debtX + avgDeltaX, this.debtY + avgDeltaY))
|
|
195
|
+
return this.getMaxDeltaDebtInRange(lowDeltaX, lowDeltaY, avgDeltaX, avgDeltaY);
|
|
196
|
+
else
|
|
197
|
+
return this.getMaxDeltaDebtInRange(avgDeltaX, avgDeltaY, highDeltaX, highDeltaY);
|
|
198
|
+
}
|
|
199
|
+
getMaxDeltaLeverageInRange(lowDeltaX, lowDeltaY, highDeltaX, highDeltaY) {
|
|
200
|
+
if (Number.isNaN(lowDeltaX) || Number.isNaN(lowDeltaY) || Number.isNaN(highDeltaX) || Number.isNaN(highDeltaY))
|
|
201
|
+
return [0, 0];
|
|
202
|
+
if (highDeltaX == 0 && highDeltaY == 0)
|
|
203
|
+
return [0, 0];
|
|
204
|
+
if (Math.abs(highDeltaX / lowDeltaX) < 1.001 || Math.abs(highDeltaY / lowDeltaY) < 1.001)
|
|
205
|
+
return [lowDeltaX, lowDeltaY];
|
|
206
|
+
const avgDeltaX = (lowDeltaX + highDeltaX) / 2;
|
|
207
|
+
const avgDeltaY = (lowDeltaY + highDeltaY) / 2;
|
|
208
|
+
if (this.isLiquidatableGivenDeltaLeverage(avgDeltaX, avgDeltaY))
|
|
209
|
+
return this.getMaxDeltaLeverageInRange(lowDeltaX, lowDeltaY, avgDeltaX, avgDeltaY);
|
|
210
|
+
else
|
|
211
|
+
return this.getMaxDeltaLeverageInRange(avgDeltaX, avgDeltaY, highDeltaX, highDeltaY);
|
|
212
|
+
}
|
|
213
|
+
getMinLeverageInRange(lowLeverage, highLeverage) {
|
|
214
|
+
if (highLeverage / lowLeverage < 1.001)
|
|
215
|
+
return highLeverage;
|
|
216
|
+
const avgLeverage = (lowLeverage + highLeverage) / 2;
|
|
217
|
+
if (this.getOptimalWithdrawForDeleverage(avgLeverage) !== null)
|
|
218
|
+
return this.getMinLeverageInRange(lowLeverage, avgLeverage);
|
|
219
|
+
else
|
|
220
|
+
return this.getMinLeverageInRange(avgLeverage, highLeverage);
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* PUBLIC SETTERS
|
|
224
|
+
*/
|
|
225
|
+
depositX(amount) {
|
|
226
|
+
this.setRealX(this.getInitialRealX() + amount);
|
|
227
|
+
}
|
|
228
|
+
depositY(amount) {
|
|
229
|
+
this.setRealY(this.getInitialRealY() + amount);
|
|
230
|
+
}
|
|
231
|
+
withdrawX(amount) {
|
|
232
|
+
const percentageToRemove = amount / this.getInitialDepositedX();
|
|
233
|
+
this.setLiquidity(this.initialLiquidity * (1 - percentageToRemove));
|
|
234
|
+
}
|
|
235
|
+
withdrawY(amount) {
|
|
236
|
+
const percentageToRemove = amount / this.getInitialDepositedY();
|
|
237
|
+
this.setLiquidity(this.initialLiquidity * (1 - percentageToRemove));
|
|
238
|
+
}
|
|
239
|
+
borrowX(amount) {
|
|
240
|
+
if (amount > this.availableToBorrowX) {
|
|
241
|
+
throw new Error("Trying to borrow more than available liquidity");
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
this.setDebtX(this.initialDebtX + amount);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
borrowY(amount) {
|
|
248
|
+
if (amount > this.availableToBorrowY) {
|
|
249
|
+
throw new Error("Trying to borrow more than available liquidity");
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
this.setDebtY(this.initialDebtY + amount);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
repayX(amount) {
|
|
256
|
+
this.setDebtX(Math.max(this.initialDebtX - amount, 0));
|
|
257
|
+
}
|
|
258
|
+
repayY(amount) {
|
|
259
|
+
this.setDebtY(Math.max(this.initialDebtY - amount, 0));
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* PUBLIC GETTERS
|
|
263
|
+
*/
|
|
264
|
+
getInitialDepositedX() {
|
|
265
|
+
return this.getInitialRealX();
|
|
266
|
+
}
|
|
267
|
+
getInitialDepositedY() {
|
|
268
|
+
return this.getInitialRealY();
|
|
269
|
+
}
|
|
270
|
+
getDepositedX() {
|
|
271
|
+
return this.getRealX();
|
|
272
|
+
}
|
|
273
|
+
getDepositedY() {
|
|
274
|
+
return this.getRealY();
|
|
275
|
+
}
|
|
276
|
+
getNetX() {
|
|
277
|
+
return this.getDepositedX() - this.debtX;
|
|
278
|
+
}
|
|
279
|
+
getNetY() {
|
|
280
|
+
return this.getDepositedY() - this.debtY;
|
|
281
|
+
}
|
|
282
|
+
isLiquidatable() {
|
|
283
|
+
return this.isLiquidatableGivenPrice(this.oraclePrice);
|
|
284
|
+
}
|
|
285
|
+
isUnderwater() {
|
|
286
|
+
return this.isUnderwaterGivenPrice(this.marketPrice);
|
|
287
|
+
}
|
|
288
|
+
getLiquidationRange() {
|
|
289
|
+
const isLiquidatable = this.isLiquidatable();
|
|
290
|
+
if (isLiquidatable)
|
|
291
|
+
return undefined;
|
|
292
|
+
if (this.marketPrice < LOWEST_PRICE || this.marketPrice > HIGHEST_PRICE)
|
|
293
|
+
return undefined;
|
|
294
|
+
const lowIsLiquidatable = this.isLiquidatableGivenPrice(LOWEST_PRICE);
|
|
295
|
+
const highIsLiquidatable = this.isLiquidatableGivenPrice(HIGHEST_PRICE);
|
|
296
|
+
const priceA = this.getLiquidationPriceInRange(LOWEST_PRICE, this.marketPrice, lowIsLiquidatable, isLiquidatable);
|
|
297
|
+
const priceB = this.getLiquidationPriceInRange(this.marketPrice, HIGHEST_PRICE, isLiquidatable, highIsLiquidatable);
|
|
298
|
+
return {
|
|
299
|
+
priceA: priceA && priceA > LOWEST_PRICE ? priceA : 0,
|
|
300
|
+
priceB: priceB && priceB < HIGHEST_PRICE ? priceB : Infinity,
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
getInitialLeverage() {
|
|
304
|
+
const leverage = this.getInitialCollateralValue() / this.getInitialEquityValue();
|
|
305
|
+
if (!leverage)
|
|
306
|
+
return 1;
|
|
307
|
+
if (Number.isNaN(leverage))
|
|
308
|
+
return 1;
|
|
309
|
+
if (leverage < 1)
|
|
310
|
+
return 1;
|
|
311
|
+
return leverage;
|
|
312
|
+
}
|
|
313
|
+
getLeverage() {
|
|
314
|
+
const leverage = this.getCollateralValue() / this.getEquityValue();
|
|
315
|
+
if (!leverage)
|
|
316
|
+
return 1;
|
|
317
|
+
if (Number.isNaN(leverage))
|
|
318
|
+
return 1;
|
|
319
|
+
if (leverage < 1)
|
|
320
|
+
return 1;
|
|
321
|
+
return leverage;
|
|
322
|
+
}
|
|
323
|
+
getOptimalWithdraw(amountX, amountY, withdrawAtLeast = true) {
|
|
324
|
+
let percentageToRemove;
|
|
325
|
+
const percentageToRemoveX = amountX / this.getInitialDepositedX();
|
|
326
|
+
const percentageToRemoveY = amountY / this.getInitialDepositedY();
|
|
327
|
+
// Withdraw at least amountX and amountY
|
|
328
|
+
if (withdrawAtLeast)
|
|
329
|
+
percentageToRemove = Math.min(Math.max(percentageToRemoveX, percentageToRemoveY), 1);
|
|
330
|
+
// Withdraw at most amountX and amountY
|
|
331
|
+
else
|
|
332
|
+
percentageToRemove = Math.min(Math.min(percentageToRemoveX, percentageToRemoveY), 1);
|
|
333
|
+
percentageToRemove = percentageToRemove && !Number.isNaN(percentageToRemove) ? percentageToRemove : 0;
|
|
334
|
+
return {
|
|
335
|
+
amountX: this.getInitialDepositedX() * percentageToRemove,
|
|
336
|
+
amountY: this.getInitialDepositedY() * percentageToRemove,
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
getOptimalWithdrawForDeleverage(targetLeverage) {
|
|
340
|
+
// 1. If target leverage out of range
|
|
341
|
+
if (!(targetLeverage >= 1 && targetLeverage < this.getInitialLeverage())) {
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
344
|
+
// 2. If target leverage is achievable through a straight deleverage
|
|
345
|
+
const maxStraightDeleverage = this.getOptimalWithdraw(this.initialDebtX, this.initialDebtY, false);
|
|
346
|
+
const maxStraightDeleverageValue = this.getValueGivenAmounts(maxStraightDeleverage.amountX, maxStraightDeleverage.amountY);
|
|
347
|
+
const minStraightLeverage = (this.getInitialCollateralValue() - maxStraightDeleverageValue) / this.getInitialEquityValue();
|
|
348
|
+
if (targetLeverage >= minStraightLeverage) {
|
|
349
|
+
const desiredCollateralValue = targetLeverage * this.getInitialEquityValue();
|
|
350
|
+
const desiredValue = this.getInitialCollateralValue() - desiredCollateralValue;
|
|
351
|
+
const ratio = desiredValue / maxStraightDeleverageValue;
|
|
352
|
+
return {
|
|
353
|
+
amountX: maxStraightDeleverage.amountX * ratio,
|
|
354
|
+
amountY: maxStraightDeleverage.amountY * ratio,
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
// 3. Deleveraging will result in withdrawing
|
|
358
|
+
const debtValue = this.getInitialDebtValue() - maxStraightDeleverageValue;
|
|
359
|
+
const collateralValue = this.getInitialCollateralValue() - maxStraightDeleverageValue;
|
|
360
|
+
let repayRatio;
|
|
361
|
+
if (this.initialDebtX == maxStraightDeleverage.amountX) {
|
|
362
|
+
repayRatio = this.getValueGivenAmounts(0, this.getInitialDepositedY()) / this.getInitialCollateralValue();
|
|
363
|
+
}
|
|
364
|
+
else {
|
|
365
|
+
repayRatio = this.getValueGivenAmounts(this.getInitialDepositedX(), 0) / this.getInitialCollateralValue();
|
|
366
|
+
}
|
|
367
|
+
const deltaValue = (collateralValue * (targetLeverage - 1) - debtValue * targetLeverage) / (targetLeverage * (1 - repayRatio) - 1);
|
|
368
|
+
// Proof:
|
|
369
|
+
// leverage = newCollateral / (newCollateral - newDebt)
|
|
370
|
+
// newCollateral = collateral - delta
|
|
371
|
+
// newDebt = debt - delta * repayRatio;
|
|
372
|
+
// ((collateral - delta) - (debt - delta * repayRatio)) * leverage = (collateral - delta)
|
|
373
|
+
// (collateral - delta - debt + delta * repayRatio) * leverage - collateral = - delta
|
|
374
|
+
// collateral * leverage - delta * leverage - debt * leverage + delta * repayRatio * leverage - collateral = - delta
|
|
375
|
+
// collateral * leverage - debt * leverage - collateral = - delta + delta * leverage - delta * repayRatio * leverage
|
|
376
|
+
// collateral * (leverage-1) - debt * leverage = delta * (leverage - repayRatio * leverage - 1)
|
|
377
|
+
// collateral * (leverage-1) - debt * leverage = delta * (leverage * (1-repayRatio) - 1)
|
|
378
|
+
// (collateral * (leverage-1) - debt * leverage) / (leverage * (1-repayRatio) - 1) = delta
|
|
379
|
+
// 4. Return amounts if the leverage is achievable
|
|
380
|
+
if (deltaValue < 0 || deltaValue > collateralValue)
|
|
381
|
+
return null;
|
|
382
|
+
const percentageToRemove = (deltaValue + maxStraightDeleverageValue) / this.getInitialCollateralValue();
|
|
383
|
+
return {
|
|
384
|
+
amountX: this.getInitialDepositedX() * percentageToRemove,
|
|
385
|
+
amountY: this.getInitialDepositedY() * percentageToRemove,
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
getMaxLeverage() {
|
|
389
|
+
const currentLeverage = this.getLeverage();
|
|
390
|
+
if (this.isLiquidatable())
|
|
391
|
+
return currentLeverage;
|
|
392
|
+
const highLeverage = 100; // we assume the position is liquidatable with this leverage
|
|
393
|
+
let setLiquidityToZero = false;
|
|
394
|
+
if (this.liquidity === 0) {
|
|
395
|
+
this.liquidity = 0.000001;
|
|
396
|
+
setLiquidityToZero = true;
|
|
397
|
+
}
|
|
398
|
+
const realX = this.getRealX();
|
|
399
|
+
const realY = this.getRealY();
|
|
400
|
+
let maxDeltaX = realX * highLeverage / currentLeverage;
|
|
401
|
+
let maxDeltaY = realY * highLeverage / currentLeverage;
|
|
402
|
+
const actualAvailableToBorrowX = this.availableToBorrowX - (this.debtX - this.initialDebtX);
|
|
403
|
+
const actualAvailableToBorrowY = this.availableToBorrowY - (this.debtY - this.initialDebtY);
|
|
404
|
+
if (maxDeltaX > actualAvailableToBorrowX) {
|
|
405
|
+
maxDeltaY *= actualAvailableToBorrowX / maxDeltaX;
|
|
406
|
+
maxDeltaX = actualAvailableToBorrowX;
|
|
407
|
+
}
|
|
408
|
+
if (maxDeltaY > actualAvailableToBorrowY) {
|
|
409
|
+
maxDeltaX *= actualAvailableToBorrowY / maxDeltaY;
|
|
410
|
+
maxDeltaY = actualAvailableToBorrowY;
|
|
411
|
+
}
|
|
412
|
+
const [deltaX, deltaY] = this.getMaxDeltaLeverageInRange(0, 0, maxDeltaX, maxDeltaY);
|
|
413
|
+
const collateralValue = this.getCollateralValue();
|
|
414
|
+
const additionalValue = this.getValueGivenAmounts(deltaX, deltaY);
|
|
415
|
+
if (setLiquidityToZero)
|
|
416
|
+
this.liquidity = 0;
|
|
417
|
+
return (collateralValue + additionalValue) / collateralValue * currentLeverage;
|
|
418
|
+
}
|
|
419
|
+
getMinLeverage() {
|
|
420
|
+
const depositedX = this.getDepositedX();
|
|
421
|
+
const depositedY = this.getDepositedY();
|
|
422
|
+
const ratioX = this.debtX === 0 ? Infinity : depositedX / this.debtX;
|
|
423
|
+
const ratioY = this.debtY === 0 ? Infinity : depositedY / this.debtY;
|
|
424
|
+
if (ratioX >= 1 && ratioY >= 1)
|
|
425
|
+
return 0;
|
|
426
|
+
return this.getMinLeverageInRange(1, this.getInitialLeverage());
|
|
427
|
+
}
|
|
428
|
+
getMaxWithdrawable() {
|
|
429
|
+
const depositedX = this.getDepositedX();
|
|
430
|
+
const depositedY = this.getDepositedY();
|
|
431
|
+
const additionalDepositX = depositedX - this.getInitialDepositedX();
|
|
432
|
+
const additionalDepositY = depositedY - this.getInitialDepositedY();
|
|
433
|
+
let percentage = 0;
|
|
434
|
+
if (!this.isLiquidatable()) {
|
|
435
|
+
if (this.debtX == 0 && this.debtY == 0) {
|
|
436
|
+
percentage = 1;
|
|
437
|
+
}
|
|
438
|
+
else {
|
|
439
|
+
const highRatio = this.getEquityValue() / this.getDebtValue();
|
|
440
|
+
const [deltaDebtX, deltaDebtY] = this.getMaxDeltaDebtInRange(0, 0, this.debtX * highRatio, this.debtY * highRatio);
|
|
441
|
+
const ratio = this.debtX > 0 ? deltaDebtX / this.debtX : deltaDebtY / this.debtY;
|
|
442
|
+
percentage = 1 / (1 / ratio + 1);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
return {
|
|
446
|
+
amountX: depositedX * percentage - additionalDepositX,
|
|
447
|
+
amountY: depositedY * percentage - additionalDepositY,
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
getMaxBorrowableX() {
|
|
451
|
+
if (this.isLiquidatable())
|
|
452
|
+
return 0;
|
|
453
|
+
const equityValue = this.getEquityValue();
|
|
454
|
+
const highDebtX = this.getAmountXGivenValue(equityValue);
|
|
455
|
+
const [debtX,] = this.getMaxDeltaDebtInRange(0, 0, highDebtX, 0);
|
|
456
|
+
return Math.min(debtX + this.debtX - this.initialDebtX, this.availableToBorrowX);
|
|
457
|
+
}
|
|
458
|
+
getMaxBorrowableY() {
|
|
459
|
+
if (this.isLiquidatable())
|
|
460
|
+
return 0;
|
|
461
|
+
const equityValue = this.getEquityValue();
|
|
462
|
+
const highDebtY = this.getAmountYGivenValue(equityValue);
|
|
463
|
+
const [, debtY] = this.getMaxDeltaDebtInRange(0, 0, 0, highDebtY);
|
|
464
|
+
return Math.min(debtY + this.debtY - this.initialDebtY, this.availableToBorrowY);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
exports.GenericPosition = GenericPosition;
|
|
468
|
+
exports.default = GenericPosition;
|