@d8x/perpetuals-sdk 0.1.1 → 0.1.2
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/d8XMath.d.ts +3 -5
- package/dist/d8XMath.js +5 -7
- package/dist/marketData.d.ts +3 -3
- package/dist/marketData.js +47 -22
- package/dist/perpetualDataHandler.d.ts +8 -1
- package/dist/perpetualDataHandler.js +66 -29
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/src/d8XMath.ts +6 -12
- package/src/marketData.ts +91 -60
- package/src/perpetualDataHandler.ts +76 -28
- package/src/version.ts +1 -1
package/dist/d8XMath.d.ts
CHANGED
|
@@ -96,17 +96,15 @@ export declare function getMaxSignedPositionSize(marginCollateral: number, curre
|
|
|
96
96
|
/**
|
|
97
97
|
* Compute the leverage resulting from a trade
|
|
98
98
|
* @param tradeAmount Amount to trade, in base currency, signed
|
|
99
|
-
* @param marginCollateral Amount of cash in the margin account,
|
|
99
|
+
* @param marginCollateral Amount of cash in the margin account, in collateral currency
|
|
100
100
|
* @param currentPosition Position size before the trade
|
|
101
101
|
* @param currentLockedInValue Locked-in value before the trade
|
|
102
|
-
* @param
|
|
102
|
+
* @param price Price charged to trade tradeAmount
|
|
103
103
|
* @param indexPriceS3 Spot price of the collateral currency when the trade happens
|
|
104
104
|
* @param markPrice Mark price of the index when the trade happens
|
|
105
|
-
* @param limitPrice Price charged to trade tradeAmount
|
|
106
|
-
* @param feeRate Trading fee rate applicable to this trade
|
|
107
105
|
* @returns Leverage of the resulting position
|
|
108
106
|
*/
|
|
109
|
-
export declare function getNewPositionLeverage(tradeAmount: number, marginCollateral: number, currentPosition: number, currentLockedInValue: number,
|
|
107
|
+
export declare function getNewPositionLeverage(tradeAmount: number, marginCollateral: number, currentPosition: number, currentLockedInValue: number, price: number, indexPriceS3: number, markPrice: number): number;
|
|
110
108
|
/**
|
|
111
109
|
* Determine amount to be deposited into margin account so that the given leverage
|
|
112
110
|
* is obtained when trading a position pos (trade amount = position)
|
package/dist/d8XMath.js
CHANGED
|
@@ -224,20 +224,18 @@ exports.getMaxSignedPositionSize = getMaxSignedPositionSize;
|
|
|
224
224
|
/**
|
|
225
225
|
* Compute the leverage resulting from a trade
|
|
226
226
|
* @param tradeAmount Amount to trade, in base currency, signed
|
|
227
|
-
* @param marginCollateral Amount of cash in the margin account,
|
|
227
|
+
* @param marginCollateral Amount of cash in the margin account, in collateral currency
|
|
228
228
|
* @param currentPosition Position size before the trade
|
|
229
229
|
* @param currentLockedInValue Locked-in value before the trade
|
|
230
|
-
* @param
|
|
230
|
+
* @param price Price charged to trade tradeAmount
|
|
231
231
|
* @param indexPriceS3 Spot price of the collateral currency when the trade happens
|
|
232
232
|
* @param markPrice Mark price of the index when the trade happens
|
|
233
|
-
* @param limitPrice Price charged to trade tradeAmount
|
|
234
|
-
* @param feeRate Trading fee rate applicable to this trade
|
|
235
233
|
* @returns Leverage of the resulting position
|
|
236
234
|
*/
|
|
237
|
-
function getNewPositionLeverage(tradeAmount, marginCollateral, currentPosition, currentLockedInValue,
|
|
235
|
+
function getNewPositionLeverage(tradeAmount, marginCollateral, currentPosition, currentLockedInValue, price, indexPriceS3, markPrice) {
|
|
238
236
|
let newPosition = tradeAmount + currentPosition;
|
|
239
|
-
let pnlQC = currentPosition * markPrice - currentLockedInValue + tradeAmount * (markPrice -
|
|
240
|
-
return (
|
|
237
|
+
let pnlQC = currentPosition * markPrice - currentLockedInValue + tradeAmount * (markPrice - price);
|
|
238
|
+
return (Math.abs(newPosition) * markPrice) / (marginCollateral * indexPriceS3 + pnlQC);
|
|
241
239
|
}
|
|
242
240
|
exports.getNewPositionLeverage = getNewPositionLeverage;
|
|
243
241
|
/**
|
package/dist/marketData.d.ts
CHANGED
|
@@ -139,7 +139,7 @@ export default class MarketData extends PerpetualDataHandler {
|
|
|
139
139
|
* @param currentPositionRisk Position risk before trade
|
|
140
140
|
* @returns {MarginAccount} Position risk after trade
|
|
141
141
|
*/
|
|
142
|
-
positionRiskOnTrade(traderAddr: string, order: Order, currentPositionRisk?: MarginAccount): Promise<MarginAccount>;
|
|
142
|
+
positionRiskOnTrade(traderAddr: string, order: Order, currentPositionRisk?: MarginAccount, indexPriceInfo?: [number, number, boolean, boolean]): Promise<MarginAccount>;
|
|
143
143
|
/**
|
|
144
144
|
* Estimates what the position risk will be if given amount of collateral is added/removed from the account.
|
|
145
145
|
* @param traderAddr Address of trader
|
|
@@ -147,7 +147,7 @@ export default class MarketData extends PerpetualDataHandler {
|
|
|
147
147
|
* @param currentPositionRisk Position risk before
|
|
148
148
|
* @returns {MarginAccount} Position risk after
|
|
149
149
|
*/
|
|
150
|
-
positionRiskOnCollateralAction(deltaCollateral: number, currentPositionRisk: MarginAccount): Promise<MarginAccount>;
|
|
150
|
+
positionRiskOnCollateralAction(deltaCollateral: number, currentPositionRisk: MarginAccount, indexPriceInfo?: [number, number, boolean, boolean]): Promise<MarginAccount>;
|
|
151
151
|
protected static _positionRiskOnAccountAction(symbol: string, tradeAmount: number, marginDeposit: number, tradeLeverage: number | undefined, keepPositionLvg: boolean | undefined, tradePrice: number, feeRate: number, perpetualState: PerpetualState, currentPositionRisk: MarginAccount, symbolToPerpStaticInfo: Map<string, PerpetualStaticInfo>): MarginAccount;
|
|
152
152
|
/**
|
|
153
153
|
* Gets the wallet balance in the collateral currency corresponding to a given perpetual symbol.
|
|
@@ -278,6 +278,6 @@ export default class MarketData extends PerpetualDataHandler {
|
|
|
278
278
|
* @ignore
|
|
279
279
|
*/
|
|
280
280
|
static orderIdsOfTrader(traderAddr: string, orderBookContract: ethers.Contract): Promise<string[]>;
|
|
281
|
-
getAvailableMargin(traderAddr: string, symbol: string): Promise<number>;
|
|
281
|
+
getAvailableMargin(traderAddr: string, symbol: string, indexPrices?: [number, number]): Promise<number>;
|
|
282
282
|
static _exchangeInfo(_proxyContract: ethers.Contract, _poolStaticInfos: Array<PoolStaticInfo>, _symbolToPerpStaticInfo: Map<string, PerpetualStaticInfo>, _symbolList: Map<string, string>, _priceFeedGetter: PriceFeeds): Promise<ExchangeInfo>;
|
|
283
283
|
}
|
package/dist/marketData.js
CHANGED
|
@@ -200,7 +200,7 @@ class MarketData extends perpetualDataHandler_1.default {
|
|
|
200
200
|
* @param currentPositionRisk Position risk before trade
|
|
201
201
|
* @returns {MarginAccount} Position risk after trade
|
|
202
202
|
*/
|
|
203
|
-
positionRiskOnTrade(traderAddr, order, currentPositionRisk) {
|
|
203
|
+
positionRiskOnTrade(traderAddr, order, currentPositionRisk, indexPriceInfo) {
|
|
204
204
|
var _a, _b, _c;
|
|
205
205
|
return __awaiter(this, void 0, void 0, function* () {
|
|
206
206
|
if (this.proxyContract == null) {
|
|
@@ -209,6 +209,10 @@ class MarketData extends perpetualDataHandler_1.default {
|
|
|
209
209
|
if (currentPositionRisk == undefined) {
|
|
210
210
|
currentPositionRisk = yield this.positionRisk(traderAddr, order.symbol);
|
|
211
211
|
}
|
|
212
|
+
if (indexPriceInfo == undefined) {
|
|
213
|
+
let obj = yield this.priceFeedGetter.fetchPricesForPerpetual(currentPositionRisk.symbol);
|
|
214
|
+
indexPriceInfo = [obj.idxPrices[0], obj.idxPrices[1], obj.mktClosed[0], obj.mktClosed[1]];
|
|
215
|
+
}
|
|
212
216
|
let poolId = perpetualDataHandler_1.default._getPoolIdFromSymbol(order.symbol, this.poolStaticInfos);
|
|
213
217
|
// price for this order = limit price (conservative) if given, else the current perp price
|
|
214
218
|
let tradeAmount = Math.abs(order.quantity) * (order.side == nodeSDKTypes_1.BUY_SIDE ? 1 : -1);
|
|
@@ -217,7 +221,7 @@ class MarketData extends perpetualDataHandler_1.default {
|
|
|
217
221
|
let feeRate = ((yield this.proxyContract.queryExchangeFee(poolId, traderAddr, (_b = order.brokerAddr) !== null && _b !== void 0 ? _b : nodeSDKTypes_1.ZERO_ADDRESS)) +
|
|
218
222
|
((_c = order.brokerFeeTbps) !== null && _c !== void 0 ? _c : 0)) /
|
|
219
223
|
100000;
|
|
220
|
-
let perpetualState = yield this.getPerpetualState(order.symbol);
|
|
224
|
+
let perpetualState = yield this.getPerpetualState(order.symbol, indexPriceInfo);
|
|
221
225
|
return MarketData._positionRiskOnAccountAction(order.symbol, tradeAmount, 0, order.leverage, order.keepPositionLvg, tradePrice, feeRate, perpetualState, currentPositionRisk, this.symbolToPerpStaticInfo);
|
|
222
226
|
});
|
|
223
227
|
}
|
|
@@ -228,12 +232,16 @@ class MarketData extends perpetualDataHandler_1.default {
|
|
|
228
232
|
* @param currentPositionRisk Position risk before
|
|
229
233
|
* @returns {MarginAccount} Position risk after
|
|
230
234
|
*/
|
|
231
|
-
positionRiskOnCollateralAction(deltaCollateral, currentPositionRisk) {
|
|
235
|
+
positionRiskOnCollateralAction(deltaCollateral, currentPositionRisk, indexPriceInfo) {
|
|
232
236
|
return __awaiter(this, void 0, void 0, function* () {
|
|
233
237
|
if (this.proxyContract == null) {
|
|
234
238
|
throw Error("no proxy contract initialized. Use createProxyInstance().");
|
|
235
239
|
}
|
|
236
|
-
|
|
240
|
+
if (indexPriceInfo == undefined) {
|
|
241
|
+
let obj = yield this.priceFeedGetter.fetchPricesForPerpetual(currentPositionRisk.symbol);
|
|
242
|
+
indexPriceInfo = [obj.idxPrices[0], obj.idxPrices[1], obj.mktClosed[0], obj.mktClosed[1]];
|
|
243
|
+
}
|
|
244
|
+
let perpetualState = yield this.getPerpetualState(currentPositionRisk.symbol, indexPriceInfo);
|
|
237
245
|
return MarketData._positionRiskOnAccountAction(currentPositionRisk.symbol, 0, deltaCollateral, undefined, false, 0, 0, perpetualState, currentPositionRisk, this.symbolToPerpStaticInfo);
|
|
238
246
|
});
|
|
239
247
|
}
|
|
@@ -243,6 +251,8 @@ class MarketData extends perpetualDataHandler_1.default {
|
|
|
243
251
|
let newPosition = currentPosition + tradeAmount;
|
|
244
252
|
let newSide = newPosition > 0 ? nodeSDKTypes_1.BUY_SIDE : newPosition < 0 ? nodeSDKTypes_1.SELL_SIDE : nodeSDKTypes_1.CLOSED_SIDE;
|
|
245
253
|
let lockedInValue = currentPositionRisk.entryPrice * currentPosition;
|
|
254
|
+
let newLockedInValue = lockedInValue + tradeAmount * tradePrice;
|
|
255
|
+
let newEntryPrice = newPosition == 0 ? 0 : Math.abs(newLockedInValue / newPosition);
|
|
246
256
|
if (tradeAmount == 0) {
|
|
247
257
|
keepPositionLvg = false;
|
|
248
258
|
}
|
|
@@ -262,32 +272,35 @@ class MarketData extends perpetualDataHandler_1.default {
|
|
|
262
272
|
// this gives us the total margin needed in the account so that it satisfies the leverage condition
|
|
263
273
|
newCollateral = (0, d8XMath_1.getMarginRequiredForLeveragedTrade)(currentPositionRisk.leverage, currentPosition, lockedInValue, tradeAmount, markPrice, indexPriceS2, indexPriceS3, tradePrice, feeRate);
|
|
264
274
|
// the new leverage follows from the updated margin and position
|
|
265
|
-
newLeverage = (0, d8XMath_1.getNewPositionLeverage)(tradeAmount, newCollateral, currentPosition, lockedInValue,
|
|
275
|
+
newLeverage = (0, d8XMath_1.getNewPositionLeverage)(tradeAmount, newCollateral, currentPosition, lockedInValue, tradePrice, indexPriceS3, markPrice);
|
|
266
276
|
}
|
|
267
277
|
else if (tradeAmount != 0) {
|
|
268
|
-
let
|
|
278
|
+
let deltaCash;
|
|
269
279
|
if (!isOpen && !isFlip && !keepPositionLvgOnClose) {
|
|
270
|
-
//
|
|
271
|
-
|
|
280
|
+
// cash comes from realized pnl
|
|
281
|
+
deltaCash = (tradeAmount * (tradePrice - currentPositionRisk.entryPrice)) / indexPriceS3;
|
|
282
|
+
newLockedInValue = lockedInValue + currentPositionRisk.entryPrice * tradeAmount;
|
|
272
283
|
}
|
|
273
284
|
else {
|
|
274
285
|
// target lvg will default current lvg if not specified
|
|
275
286
|
let targetLvg = isFlip || isOpen ? tradeLeverage !== null && tradeLeverage !== void 0 ? tradeLeverage : 0 : 0;
|
|
276
287
|
let b0, pos0;
|
|
277
288
|
[b0, pos0] = isOpen ? [0, 0] : [currentPositionRisk.collateralCC, currentPosition];
|
|
278
|
-
|
|
289
|
+
// cash comes from trader wallet
|
|
290
|
+
deltaCash = (0, d8XMath_1.getDepositAmountForLvgTrade)(b0, pos0, tradeAmount, targetLvg, tradePrice, indexPriceS3, markPrice);
|
|
291
|
+
// premium/instantaneous pnl
|
|
292
|
+
// deltaCash -= (tradeAmount * (tradePrice - indexPriceS2)) / indexPriceS3;
|
|
293
|
+
newLockedInValue = lockedInValue + tradeAmount * tradePrice;
|
|
279
294
|
}
|
|
280
|
-
newCollateral = currentPositionRisk.collateralCC +
|
|
295
|
+
newCollateral = currentPositionRisk.collateralCC + deltaCash; // - (feeRate * Math.abs(tradeAmount) * indexPriceS2) / indexPriceS3;
|
|
281
296
|
// the new leverage corresponds to increasing the position and collateral according to the order
|
|
282
|
-
newLeverage = (0, d8XMath_1.getNewPositionLeverage)(
|
|
297
|
+
newLeverage = (0, d8XMath_1.getNewPositionLeverage)(0, newCollateral, newPosition, newLockedInValue, tradePrice, indexPriceS3, markPrice);
|
|
283
298
|
}
|
|
284
299
|
else {
|
|
285
300
|
// there is no order, adding/removing collateral
|
|
286
301
|
newCollateral = currentPositionRisk.collateralCC + marginDeposit;
|
|
287
|
-
newLeverage = (0, d8XMath_1.getNewPositionLeverage)(0, newCollateral, currentPosition, lockedInValue, indexPriceS2, indexPriceS3, markPrice
|
|
302
|
+
newLeverage = (0, d8XMath_1.getNewPositionLeverage)(0, newCollateral, currentPosition, lockedInValue, indexPriceS2, indexPriceS3, markPrice);
|
|
288
303
|
}
|
|
289
|
-
let newLockedInValue = lockedInValue + tradeAmount * tradePrice;
|
|
290
|
-
let entryPrice = newPosition == 0 ? 0 : Math.abs(newLockedInValue / newPosition);
|
|
291
304
|
// liquidation vars
|
|
292
305
|
let S2Liq, S3Liq;
|
|
293
306
|
let tau = symbolToPerpStaticInfo.get(symbol).maintenanceMarginRate;
|
|
@@ -307,7 +320,7 @@ class MarketData extends perpetualDataHandler_1.default {
|
|
|
307
320
|
symbol: currentPositionRisk.symbol,
|
|
308
321
|
positionNotionalBaseCCY: Math.abs(newPosition),
|
|
309
322
|
side: newSide,
|
|
310
|
-
entryPrice:
|
|
323
|
+
entryPrice: newEntryPrice,
|
|
311
324
|
leverage: newLeverage,
|
|
312
325
|
markPrice: markPrice,
|
|
313
326
|
unrealizedPnlQuoteCCY: newPosition * markPrice - newLockedInValue,
|
|
@@ -571,14 +584,22 @@ class MarketData extends perpetualDataHandler_1.default {
|
|
|
571
584
|
return digests;
|
|
572
585
|
});
|
|
573
586
|
}
|
|
574
|
-
getAvailableMargin(traderAddr, symbol) {
|
|
587
|
+
getAvailableMargin(traderAddr, symbol, indexPrices) {
|
|
575
588
|
return __awaiter(this, void 0, void 0, function* () {
|
|
576
589
|
if (this.proxyContract == null) {
|
|
577
590
|
throw Error("no proxy contract initialized. Use createProxyInstance().");
|
|
578
591
|
}
|
|
579
592
|
let mgnAcct = yield perpetualDataHandler_1.default.getMarginAccount(traderAddr, symbol, this.symbolToPerpStaticInfo, this.proxyContract);
|
|
593
|
+
if (indexPrices == undefined) {
|
|
594
|
+
// fetch from API
|
|
595
|
+
let obj = yield this.priceFeedGetter.fetchPricesForPerpetual(symbol);
|
|
596
|
+
indexPrices = [obj.idxPrices[0], obj.idxPrices[1]];
|
|
597
|
+
}
|
|
598
|
+
let S2 = indexPrices[0];
|
|
599
|
+
let ccyType = this.getPerpetualStaticInfo(symbol).collateralCurrencyType;
|
|
600
|
+
let S3 = ccyType == nodeSDKTypes_1.COLLATERAL_CURRENCY_QUANTO ? indexPrices[1] : ccyType == nodeSDKTypes_1.COLLATERAL_CURRENCY_QUOTE ? 1 : S2;
|
|
580
601
|
let perpInfo = this.symbolToPerpStaticInfo.get(symbol);
|
|
581
|
-
let balanceCC = mgnAcct.collateralCC + mgnAcct.unrealizedPnlQuoteCCY /
|
|
602
|
+
let balanceCC = mgnAcct.collateralCC + mgnAcct.unrealizedPnlQuoteCCY / S3;
|
|
582
603
|
let initalMarginCC = Math.abs((perpInfo.initialMarginRate * mgnAcct.positionNotionalBaseCCY * mgnAcct.markPrice) /
|
|
583
604
|
mgnAcct.collToQuoteConversion);
|
|
584
605
|
return balanceCC - initalMarginCC;
|
|
@@ -595,7 +616,7 @@ class MarketData extends perpetualDataHandler_1.default {
|
|
|
595
616
|
for (let perpSymbol of _symbolToPerpStaticInfo.keys()) {
|
|
596
617
|
let sInfo = _symbolToPerpStaticInfo.get(perpSymbol);
|
|
597
618
|
allSym.add(sInfo.S2Symbol);
|
|
598
|
-
if (sInfo.S3Symbol !=
|
|
619
|
+
if (sInfo.S3Symbol != "") {
|
|
599
620
|
allSym.add(sInfo.S3Symbol);
|
|
600
621
|
}
|
|
601
622
|
}
|
|
@@ -619,8 +640,12 @@ class MarketData extends perpetualDataHandler_1.default {
|
|
|
619
640
|
let poolSymbol = PoolState.poolSymbol;
|
|
620
641
|
for (var k = 0; k < perpetualIDs.length; k++) {
|
|
621
642
|
let perp = yield _proxyContract.getPerpetual(perpetualIDs[k]);
|
|
622
|
-
let symS2 = (0, utils_1.contractSymbolToSymbol)(perp.S2BaseCCY, _symbolList) +
|
|
623
|
-
|
|
643
|
+
let symS2 = (0, utils_1.contractSymbolToSymbol)(perp.S2BaseCCY, _symbolList) +
|
|
644
|
+
"-" +
|
|
645
|
+
(0, utils_1.contractSymbolToSymbol)(perp.S2QuoteCCY, _symbolList);
|
|
646
|
+
let symS3 = (0, utils_1.contractSymbolToSymbol)(perp.S3BaseCCY, _symbolList) +
|
|
647
|
+
"-" +
|
|
648
|
+
(0, utils_1.contractSymbolToSymbol)(perp.S3QuoteCCY, _symbolList);
|
|
624
649
|
let perpSymbol = symS2 + "-" + poolSymbol;
|
|
625
650
|
//console.log("perpsymbol=",perpSymbol);
|
|
626
651
|
let res = idxPriceMap.get(symS2);
|
|
@@ -645,7 +670,7 @@ class MarketData extends perpetualDataHandler_1.default {
|
|
|
645
670
|
}
|
|
646
671
|
let fMidPrice = yield _proxyContract.queryPerpetualPrice(perpetualIDs[k], ethers_1.BigNumber.from(0), [
|
|
647
672
|
(0, d8XMath_1.floatToABK64x64)(indexS2),
|
|
648
|
-
(0, d8XMath_1.floatToABK64x64)(indexS3)
|
|
673
|
+
(0, d8XMath_1.floatToABK64x64)(indexS3),
|
|
649
674
|
]);
|
|
650
675
|
let markPremiumRate = (0, d8XMath_1.ABK64x64ToFloat)(perp.currentMarkPremiumRate.fPrice);
|
|
651
676
|
let currentFundingRateBps = 1e4 * (0, d8XMath_1.ABK64x64ToFloat)(perp.fCurrentFundingRate);
|
|
@@ -662,7 +687,7 @@ class MarketData extends perpetualDataHandler_1.default {
|
|
|
662
687
|
currentFundingRateBps: currentFundingRateBps,
|
|
663
688
|
openInterestBC: (0, d8XMath_1.ABK64x64ToFloat)(perp.fOpenInterest),
|
|
664
689
|
maxPositionBC: Infinity,
|
|
665
|
-
isMarketClosed: isS2MktClosed || isS3MktClosed
|
|
690
|
+
isMarketClosed: isS2MktClosed || isS3MktClosed,
|
|
666
691
|
};
|
|
667
692
|
PoolState.perpetuals.push(PerpetualState);
|
|
668
693
|
}
|
|
@@ -94,7 +94,7 @@ export default class PerpetualDataHandler {
|
|
|
94
94
|
protected static _getSymbolFromPoolId(poolId: number, staticInfos: PoolStaticInfo[]): string;
|
|
95
95
|
protected static _getPoolIdFromSymbol(symbol: string, staticInfos: PoolStaticInfo[]): number;
|
|
96
96
|
static getNestedPerpetualIds(_proxyContract: ethers.Contract): Promise<number[][]>;
|
|
97
|
-
static getMarginAccount(traderAddr: string, symbol: string, symbolToPerpStaticInfo: Map<string, PerpetualStaticInfo>, _proxyContract: ethers.Contract): Promise<MarginAccount>;
|
|
97
|
+
static getMarginAccount(traderAddr: string, symbol: string, symbolToPerpStaticInfo: Map<string, PerpetualStaticInfo>, _proxyContract: ethers.Contract, _pxS2S3?: [number, number | undefined]): Promise<MarginAccount>;
|
|
98
98
|
protected static _queryPerpetualPrice(symbol: string, tradeAmount: number, symbolToPerpStaticInfo: Map<string, PerpetualStaticInfo>, _proxyContract: ethers.Contract, indexPrices: [number, number]): Promise<number>;
|
|
99
99
|
protected static _queryPerpetualMarkPrice(symbol: string, symbolToPerpStaticInfo: Map<string, PerpetualStaticInfo>, _proxyContract: ethers.Contract, indexPrices: [number, number]): Promise<number>;
|
|
100
100
|
protected static _queryPerpetualState(symbol: string, symbolToPerpStaticInfo: Map<string, PerpetualStaticInfo>, _proxyContract: ethers.Contract, indexPrices: [number, number, boolean, boolean]): Promise<PerpetualState>;
|
|
@@ -164,4 +164,11 @@ export default class PerpetualDataHandler {
|
|
|
164
164
|
*/
|
|
165
165
|
getPoolIndexFromSymbol(symbol: string): number;
|
|
166
166
|
getMarginTokenFromSymbol(symbol: string): string | undefined;
|
|
167
|
+
/**
|
|
168
|
+
* Performs basic validity checks on a given order
|
|
169
|
+
* @param order Order struct
|
|
170
|
+
* @param traderAccount Trader account
|
|
171
|
+
* @param perpStaticInfo Symbol to perpetual info map
|
|
172
|
+
*/
|
|
173
|
+
protected static checkOrder(order: Order, traderAccount: MarginAccount, perpStaticInfo: Map<string, PerpetualStaticInfo>): void;
|
|
167
174
|
}
|
|
@@ -111,23 +111,21 @@ class PerpetualDataHandler {
|
|
|
111
111
|
// try to find a limit order book
|
|
112
112
|
let lobAddr = yield this.lobFactoryContract.getOrderBookAddress(perpetualIDs[k]);
|
|
113
113
|
currentLimitOrderBookAddr.push(lobAddr);
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
ccy.push(nodeSDKTypes_1.CollaterlCCY.QUANTO);
|
|
130
|
-
}
|
|
114
|
+
// we find out the pool currency by looking at all perpetuals
|
|
115
|
+
// unless for quanto perpetuals, we know the pool currency
|
|
116
|
+
// from the perpetual. This fails if we have a pool with only
|
|
117
|
+
// quanto perpetuals
|
|
118
|
+
if (perp.eCollateralCurrency == nodeSDKTypes_1.COLLATERAL_CURRENCY_BASE) {
|
|
119
|
+
poolCCY = poolCCY !== null && poolCCY !== void 0 ? poolCCY : base;
|
|
120
|
+
ccy.push(nodeSDKTypes_1.CollaterlCCY.BASE);
|
|
121
|
+
}
|
|
122
|
+
else if (perp.eCollateralCurrency == nodeSDKTypes_1.COLLATERAL_CURRENCY_QUOTE) {
|
|
123
|
+
poolCCY = poolCCY !== null && poolCCY !== void 0 ? poolCCY : quote;
|
|
124
|
+
ccy.push(nodeSDKTypes_1.CollaterlCCY.QUOTE);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
poolCCY = poolCCY !== null && poolCCY !== void 0 ? poolCCY : base3;
|
|
128
|
+
ccy.push(nodeSDKTypes_1.CollaterlCCY.QUANTO);
|
|
131
129
|
}
|
|
132
130
|
}
|
|
133
131
|
if (perpetualIDs.length == 0) {
|
|
@@ -286,12 +284,16 @@ class PerpetualDataHandler {
|
|
|
286
284
|
return poolIds;
|
|
287
285
|
});
|
|
288
286
|
}
|
|
289
|
-
static getMarginAccount(traderAddr, symbol, symbolToPerpStaticInfo, _proxyContract) {
|
|
287
|
+
static getMarginAccount(traderAddr, symbol, symbolToPerpStaticInfo, _proxyContract, _pxS2S3) {
|
|
290
288
|
return __awaiter(this, void 0, void 0, function* () {
|
|
291
289
|
let perpId = Number(symbol);
|
|
292
290
|
if (isNaN(perpId)) {
|
|
293
291
|
perpId = PerpetualDataHandler.symbolToPerpetualId(symbol, symbolToPerpStaticInfo);
|
|
294
292
|
}
|
|
293
|
+
// TODO: correct numbers using actual S2, S3
|
|
294
|
+
// if (_pxS2S3 == undefined) {
|
|
295
|
+
// // fetch s2,s3
|
|
296
|
+
// }
|
|
295
297
|
const idx_cash = 3;
|
|
296
298
|
const idx_notional = 4;
|
|
297
299
|
const idx_locked_in = 5;
|
|
@@ -342,20 +344,26 @@ class PerpetualDataHandler {
|
|
|
342
344
|
});
|
|
343
345
|
}
|
|
344
346
|
static _queryPerpetualState(symbol, symbolToPerpStaticInfo, _proxyContract, indexPrices) {
|
|
345
|
-
var _a, _b;
|
|
346
347
|
return __awaiter(this, void 0, void 0, function* () {
|
|
347
348
|
let perpId = PerpetualDataHandler.symbolToPerpetualId(symbol, symbolToPerpStaticInfo);
|
|
349
|
+
let staticInfo = symbolToPerpStaticInfo.get(symbol);
|
|
348
350
|
let ccy = symbol.split("-");
|
|
349
351
|
let [S2, S3] = [indexPrices[0], indexPrices[1]];
|
|
350
|
-
|
|
351
|
-
|
|
352
|
+
if (staticInfo.collateralCurrencyType == nodeSDKTypes_1.CollaterlCCY.BASE) {
|
|
353
|
+
S3 = S2;
|
|
354
|
+
}
|
|
355
|
+
else if (staticInfo.collateralCurrencyType == nodeSDKTypes_1.CollaterlCCY.QUOTE) {
|
|
356
|
+
S3 = 1;
|
|
357
|
+
}
|
|
358
|
+
let ammState = yield _proxyContract.getAMMState(perpId, [S2, S3].map(d8XMath_1.floatToABK64x64));
|
|
359
|
+
let markPrice = S2 * (1 + (0, d8XMath_1.ABK64x64ToFloat)(ammState[8]));
|
|
352
360
|
let state = {
|
|
353
361
|
id: perpId,
|
|
354
362
|
state: nodeSDKTypes_1.PERP_STATE_STR[ammState[13]],
|
|
355
363
|
baseCurrency: ccy[0],
|
|
356
364
|
quoteCurrency: ccy[1],
|
|
357
|
-
indexPrice:
|
|
358
|
-
collToQuoteIndexPrice:
|
|
365
|
+
indexPrice: S2,
|
|
366
|
+
collToQuoteIndexPrice: S3,
|
|
359
367
|
markPrice: markPrice,
|
|
360
368
|
midPrice: (0, d8XMath_1.ABK64x64ToFloat)(ammState[10]),
|
|
361
369
|
currentFundingRateBps: (0, d8XMath_1.ABK64x64ToFloat)(ammState[14]) * 1e4,
|
|
@@ -363,12 +371,6 @@ class PerpetualDataHandler {
|
|
|
363
371
|
maxPositionBC: (0, d8XMath_1.ABK64x64ToFloat)(ammState[12]),
|
|
364
372
|
isMarketClosed: indexPrices[2] || indexPrices[3],
|
|
365
373
|
};
|
|
366
|
-
if (((_a = symbolToPerpStaticInfo.get(symbol)) === null || _a === void 0 ? void 0 : _a.collateralCurrencyType) == nodeSDKTypes_1.CollaterlCCY.BASE) {
|
|
367
|
-
state.collToQuoteIndexPrice = state.indexPrice;
|
|
368
|
-
}
|
|
369
|
-
else if (((_b = symbolToPerpStaticInfo.get(symbol)) === null || _b === void 0 ? void 0 : _b.collateralCurrencyType) == nodeSDKTypes_1.CollaterlCCY.QUOTE) {
|
|
370
|
-
state.collToQuoteIndexPrice = 1;
|
|
371
|
-
}
|
|
372
374
|
return state;
|
|
373
375
|
});
|
|
374
376
|
}
|
|
@@ -720,5 +722,40 @@ class PerpetualDataHandler {
|
|
|
720
722
|
}
|
|
721
723
|
return undefined;
|
|
722
724
|
}
|
|
725
|
+
/**
|
|
726
|
+
* Performs basic validity checks on a given order
|
|
727
|
+
* @param order Order struct
|
|
728
|
+
* @param traderAccount Trader account
|
|
729
|
+
* @param perpStaticInfo Symbol to perpetual info map
|
|
730
|
+
*/
|
|
731
|
+
static checkOrder(order, traderAccount, perpStaticInfo) {
|
|
732
|
+
// this throws error if not found
|
|
733
|
+
let perpetualId = PerpetualDataHandler.symbolToPerpetualId(order.symbol, perpStaticInfo);
|
|
734
|
+
// check side
|
|
735
|
+
if (order.side != nodeSDKTypes_1.BUY_SIDE && order.side != nodeSDKTypes_1.SELL_SIDE) {
|
|
736
|
+
throw Error(`order side must be ${nodeSDKTypes_1.BUY_SIDE} or ${nodeSDKTypes_1.SELL_SIDE}`);
|
|
737
|
+
}
|
|
738
|
+
// check amount
|
|
739
|
+
let lotSize = perpStaticInfo.get(order.symbol).lotSizeBC;
|
|
740
|
+
let curPos = traderAccount.side == nodeSDKTypes_1.CLOSED_SIDE
|
|
741
|
+
? 0
|
|
742
|
+
: (traderAccount.side == nodeSDKTypes_1.BUY_SIDE ? 1 : -1) * traderAccount.positionNotionalBaseCCY;
|
|
743
|
+
let newPos = curPos + (order.side == nodeSDKTypes_1.BUY_SIDE ? 1 : -1) * order.quantity;
|
|
744
|
+
if (Math.abs(order.quantity) < lotSize || (Math.abs(newPos) >= lotSize && Math.abs(newPos) < 10 * lotSize)) {
|
|
745
|
+
throw Error(`trade amount too small: ${order.quantity} ${perpStaticInfo.get(order.symbol).S2Symbol}`);
|
|
746
|
+
}
|
|
747
|
+
// check limit price
|
|
748
|
+
if (order.side == nodeSDKTypes_1.BUY_SIDE && order.limitPrice != undefined && order.limitPrice <= 0) {
|
|
749
|
+
throw Error(`invalid limit price for buy order: ${order.limitPrice}`);
|
|
750
|
+
}
|
|
751
|
+
// broker fee
|
|
752
|
+
if (order.brokerFeeTbps != undefined && order.brokerFeeTbps < 0) {
|
|
753
|
+
throw Error(`invalid broker fee: ${order.brokerFeeTbps / 10} bps`);
|
|
754
|
+
}
|
|
755
|
+
// stop price
|
|
756
|
+
if (order.stopPrice != undefined && order.stopPrice < 0) {
|
|
757
|
+
throw Error(`invalid stop price: ${order.stopPrice}`);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
723
760
|
}
|
|
724
761
|
exports.default = PerpetualDataHandler;
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const D8X_SDK_VERSION = "0.1.
|
|
1
|
+
export declare const D8X_SDK_VERSION = "0.1.2";
|
package/dist/version.js
CHANGED
package/package.json
CHANGED
package/src/d8XMath.ts
CHANGED
|
@@ -265,14 +265,12 @@ export function getMaxSignedPositionSize(
|
|
|
265
265
|
/**
|
|
266
266
|
* Compute the leverage resulting from a trade
|
|
267
267
|
* @param tradeAmount Amount to trade, in base currency, signed
|
|
268
|
-
* @param marginCollateral Amount of cash in the margin account,
|
|
268
|
+
* @param marginCollateral Amount of cash in the margin account, in collateral currency
|
|
269
269
|
* @param currentPosition Position size before the trade
|
|
270
270
|
* @param currentLockedInValue Locked-in value before the trade
|
|
271
|
-
* @param
|
|
271
|
+
* @param price Price charged to trade tradeAmount
|
|
272
272
|
* @param indexPriceS3 Spot price of the collateral currency when the trade happens
|
|
273
273
|
* @param markPrice Mark price of the index when the trade happens
|
|
274
|
-
* @param limitPrice Price charged to trade tradeAmount
|
|
275
|
-
* @param feeRate Trading fee rate applicable to this trade
|
|
276
274
|
* @returns Leverage of the resulting position
|
|
277
275
|
*/
|
|
278
276
|
export function getNewPositionLeverage(
|
|
@@ -280,17 +278,13 @@ export function getNewPositionLeverage(
|
|
|
280
278
|
marginCollateral: number,
|
|
281
279
|
currentPosition: number,
|
|
282
280
|
currentLockedInValue: number,
|
|
283
|
-
|
|
281
|
+
price: number,
|
|
284
282
|
indexPriceS3: number,
|
|
285
|
-
markPrice: number
|
|
286
|
-
limitPrice: number,
|
|
287
|
-
feeRate: number
|
|
283
|
+
markPrice: number
|
|
288
284
|
): number {
|
|
289
285
|
let newPosition = tradeAmount + currentPosition;
|
|
290
|
-
let pnlQC = currentPosition * markPrice - currentLockedInValue + tradeAmount * (markPrice -
|
|
291
|
-
return (
|
|
292
|
-
(Math.abs(newPosition) * indexPriceS2) / (marginCollateral * indexPriceS3 + pnlQC - feeRate * Math.abs(tradeAmount))
|
|
293
|
-
);
|
|
286
|
+
let pnlQC = currentPosition * markPrice - currentLockedInValue + tradeAmount * (markPrice - price);
|
|
287
|
+
return (Math.abs(newPosition) * markPrice) / (marginCollateral * indexPriceS3 + pnlQC);
|
|
294
288
|
}
|
|
295
289
|
|
|
296
290
|
/**
|
package/src/marketData.ts
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
CLOSED_SIDE,
|
|
17
17
|
COLLATERAL_CURRENCY_BASE,
|
|
18
18
|
COLLATERAL_CURRENCY_QUANTO,
|
|
19
|
+
COLLATERAL_CURRENCY_QUOTE,
|
|
19
20
|
CollaterlCCY,
|
|
20
21
|
ERC20_ABI,
|
|
21
22
|
ExchangeInfo,
|
|
@@ -146,7 +147,13 @@ export default class MarketData extends PerpetualDataHandler {
|
|
|
146
147
|
if (this.proxyContract == null) {
|
|
147
148
|
throw Error("no proxy contract initialized. Use createProxyInstance().");
|
|
148
149
|
}
|
|
149
|
-
return await MarketData._exchangeInfo(
|
|
150
|
+
return await MarketData._exchangeInfo(
|
|
151
|
+
this.proxyContract,
|
|
152
|
+
this.poolStaticInfos,
|
|
153
|
+
this.symbolToPerpStaticInfo,
|
|
154
|
+
this.symbolList,
|
|
155
|
+
this.priceFeedGetter
|
|
156
|
+
);
|
|
150
157
|
}
|
|
151
158
|
|
|
152
159
|
/**
|
|
@@ -224,7 +231,8 @@ export default class MarketData extends PerpetualDataHandler {
|
|
|
224
231
|
public async positionRiskOnTrade(
|
|
225
232
|
traderAddr: string,
|
|
226
233
|
order: Order,
|
|
227
|
-
currentPositionRisk?: MarginAccount
|
|
234
|
+
currentPositionRisk?: MarginAccount,
|
|
235
|
+
indexPriceInfo?: [number, number, boolean, boolean]
|
|
228
236
|
): Promise<MarginAccount> {
|
|
229
237
|
if (this.proxyContract == null) {
|
|
230
238
|
throw Error("no proxy contract initialized. Use createProxyInstance().");
|
|
@@ -232,6 +240,10 @@ export default class MarketData extends PerpetualDataHandler {
|
|
|
232
240
|
if (currentPositionRisk == undefined) {
|
|
233
241
|
currentPositionRisk = await this.positionRisk(traderAddr, order.symbol);
|
|
234
242
|
}
|
|
243
|
+
if (indexPriceInfo == undefined) {
|
|
244
|
+
let obj = await this.priceFeedGetter.fetchPricesForPerpetual(currentPositionRisk.symbol);
|
|
245
|
+
indexPriceInfo = [obj.idxPrices[0], obj.idxPrices[1], obj.mktClosed[0], obj.mktClosed[1]];
|
|
246
|
+
}
|
|
235
247
|
let poolId = PerpetualDataHandler._getPoolIdFromSymbol(order.symbol, this.poolStaticInfos);
|
|
236
248
|
// price for this order = limit price (conservative) if given, else the current perp price
|
|
237
249
|
let tradeAmount = Math.abs(order.quantity) * (order.side == BUY_SIDE ? 1 : -1);
|
|
@@ -241,7 +253,7 @@ export default class MarketData extends PerpetualDataHandler {
|
|
|
241
253
|
((await this.proxyContract.queryExchangeFee(poolId, traderAddr, order.brokerAddr ?? ZERO_ADDRESS)) +
|
|
242
254
|
(order.brokerFeeTbps ?? 0)) /
|
|
243
255
|
100_000;
|
|
244
|
-
let perpetualState = await this.getPerpetualState(order.symbol);
|
|
256
|
+
let perpetualState = await this.getPerpetualState(order.symbol, indexPriceInfo);
|
|
245
257
|
|
|
246
258
|
return MarketData._positionRiskOnAccountAction(
|
|
247
259
|
order.symbol,
|
|
@@ -266,12 +278,17 @@ export default class MarketData extends PerpetualDataHandler {
|
|
|
266
278
|
*/
|
|
267
279
|
public async positionRiskOnCollateralAction(
|
|
268
280
|
deltaCollateral: number,
|
|
269
|
-
currentPositionRisk: MarginAccount
|
|
281
|
+
currentPositionRisk: MarginAccount,
|
|
282
|
+
indexPriceInfo?: [number, number, boolean, boolean]
|
|
270
283
|
): Promise<MarginAccount> {
|
|
271
284
|
if (this.proxyContract == null) {
|
|
272
285
|
throw Error("no proxy contract initialized. Use createProxyInstance().");
|
|
273
286
|
}
|
|
274
|
-
|
|
287
|
+
if (indexPriceInfo == undefined) {
|
|
288
|
+
let obj = await this.priceFeedGetter.fetchPricesForPerpetual(currentPositionRisk.symbol);
|
|
289
|
+
indexPriceInfo = [obj.idxPrices[0], obj.idxPrices[1], obj.mktClosed[0], obj.mktClosed[1]];
|
|
290
|
+
}
|
|
291
|
+
let perpetualState = await this.getPerpetualState(currentPositionRisk.symbol, indexPriceInfo);
|
|
275
292
|
|
|
276
293
|
return MarketData._positionRiskOnAccountAction(
|
|
277
294
|
currentPositionRisk.symbol,
|
|
@@ -304,6 +321,8 @@ export default class MarketData extends PerpetualDataHandler {
|
|
|
304
321
|
let newPosition = currentPosition + tradeAmount;
|
|
305
322
|
let newSide = newPosition > 0 ? BUY_SIDE : newPosition < 0 ? SELL_SIDE : CLOSED_SIDE;
|
|
306
323
|
let lockedInValue = currentPositionRisk.entryPrice * currentPosition;
|
|
324
|
+
let newLockedInValue = lockedInValue + tradeAmount * tradePrice;
|
|
325
|
+
let newEntryPrice = newPosition == 0 ? 0 : Math.abs(newLockedInValue / newPosition);
|
|
307
326
|
if (tradeAmount == 0) {
|
|
308
327
|
keepPositionLvg = false;
|
|
309
328
|
}
|
|
@@ -338,44 +357,37 @@ export default class MarketData extends PerpetualDataHandler {
|
|
|
338
357
|
newCollateral,
|
|
339
358
|
currentPosition,
|
|
340
359
|
lockedInValue,
|
|
341
|
-
indexPriceS2,
|
|
342
|
-
indexPriceS3,
|
|
343
|
-
markPrice,
|
|
344
360
|
tradePrice,
|
|
345
|
-
|
|
361
|
+
indexPriceS3,
|
|
362
|
+
markPrice
|
|
346
363
|
);
|
|
347
364
|
} else if (tradeAmount != 0) {
|
|
348
|
-
let
|
|
365
|
+
let deltaCash: number;
|
|
349
366
|
if (!isOpen && !isFlip && !keepPositionLvgOnClose) {
|
|
350
|
-
//
|
|
351
|
-
|
|
367
|
+
// cash comes from realized pnl
|
|
368
|
+
deltaCash = (tradeAmount * (tradePrice - currentPositionRisk.entryPrice)) / indexPriceS3;
|
|
369
|
+
newLockedInValue = lockedInValue + currentPositionRisk.entryPrice * tradeAmount;
|
|
352
370
|
} else {
|
|
353
371
|
// target lvg will default current lvg if not specified
|
|
354
372
|
let targetLvg = isFlip || isOpen ? tradeLeverage ?? 0 : 0;
|
|
355
373
|
let b0, pos0;
|
|
356
374
|
[b0, pos0] = isOpen ? [0, 0] : [currentPositionRisk.collateralCC, currentPosition];
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
tradePrice,
|
|
363
|
-
indexPriceS2,
|
|
364
|
-
markPrice
|
|
365
|
-
);
|
|
375
|
+
// cash comes from trader wallet
|
|
376
|
+
deltaCash = getDepositAmountForLvgTrade(b0, pos0, tradeAmount, targetLvg, tradePrice, indexPriceS3, markPrice);
|
|
377
|
+
// premium/instantaneous pnl
|
|
378
|
+
// deltaCash -= (tradeAmount * (tradePrice - indexPriceS2)) / indexPriceS3;
|
|
379
|
+
newLockedInValue = lockedInValue + tradeAmount * tradePrice;
|
|
366
380
|
}
|
|
367
|
-
newCollateral = currentPositionRisk.collateralCC +
|
|
381
|
+
newCollateral = currentPositionRisk.collateralCC + deltaCash; // - (feeRate * Math.abs(tradeAmount) * indexPriceS2) / indexPriceS3;
|
|
368
382
|
// the new leverage corresponds to increasing the position and collateral according to the order
|
|
369
383
|
newLeverage = getNewPositionLeverage(
|
|
370
|
-
|
|
384
|
+
0,
|
|
371
385
|
newCollateral,
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
indexPriceS2,
|
|
375
|
-
indexPriceS3,
|
|
376
|
-
markPrice,
|
|
386
|
+
newPosition,
|
|
387
|
+
newLockedInValue,
|
|
377
388
|
tradePrice,
|
|
378
|
-
|
|
389
|
+
indexPriceS3,
|
|
390
|
+
markPrice
|
|
379
391
|
);
|
|
380
392
|
} else {
|
|
381
393
|
// there is no order, adding/removing collateral
|
|
@@ -387,13 +399,10 @@ export default class MarketData extends PerpetualDataHandler {
|
|
|
387
399
|
lockedInValue,
|
|
388
400
|
indexPriceS2,
|
|
389
401
|
indexPriceS3,
|
|
390
|
-
markPrice
|
|
391
|
-
0,
|
|
392
|
-
0
|
|
402
|
+
markPrice
|
|
393
403
|
);
|
|
394
404
|
}
|
|
395
|
-
|
|
396
|
-
let entryPrice = newPosition == 0 ? 0 : Math.abs(newLockedInValue / newPosition);
|
|
405
|
+
|
|
397
406
|
// liquidation vars
|
|
398
407
|
let S2Liq: number, S3Liq: number | undefined;
|
|
399
408
|
let tau = symbolToPerpStaticInfo.get(symbol)!.maintenanceMarginRate;
|
|
@@ -419,7 +428,7 @@ export default class MarketData extends PerpetualDataHandler {
|
|
|
419
428
|
symbol: currentPositionRisk.symbol,
|
|
420
429
|
positionNotionalBaseCCY: Math.abs(newPosition),
|
|
421
430
|
side: newSide,
|
|
422
|
-
entryPrice:
|
|
431
|
+
entryPrice: newEntryPrice,
|
|
423
432
|
leverage: newLeverage,
|
|
424
433
|
markPrice: markPrice,
|
|
425
434
|
unrealizedPnlQuoteCCY: newPosition * markPrice - newLockedInValue,
|
|
@@ -549,11 +558,16 @@ export default class MarketData extends PerpetualDataHandler {
|
|
|
549
558
|
if (this.proxyContract == null) {
|
|
550
559
|
throw Error("no proxy contract initialized. Use createProxyInstance().");
|
|
551
560
|
}
|
|
552
|
-
if(indexPrices==undefined) {
|
|
561
|
+
if (indexPrices == undefined) {
|
|
553
562
|
let obj = await this.priceFeedGetter.fetchPricesForPerpetual(symbol);
|
|
554
563
|
indexPrices = [obj.idxPrices[0], obj.idxPrices[1]];
|
|
555
564
|
}
|
|
556
|
-
return await PerpetualDataHandler._queryPerpetualMarkPrice(
|
|
565
|
+
return await PerpetualDataHandler._queryPerpetualMarkPrice(
|
|
566
|
+
symbol,
|
|
567
|
+
this.symbolToPerpStaticInfo,
|
|
568
|
+
this.proxyContract,
|
|
569
|
+
indexPrices
|
|
570
|
+
);
|
|
557
571
|
}
|
|
558
572
|
|
|
559
573
|
/**
|
|
@@ -576,11 +590,11 @@ export default class MarketData extends PerpetualDataHandler {
|
|
|
576
590
|
*
|
|
577
591
|
* @returns price (number)
|
|
578
592
|
*/
|
|
579
|
-
public async getPerpetualPrice(symbol: string, quantity: number, indexPrices?:[number, number]): Promise<number> {
|
|
593
|
+
public async getPerpetualPrice(symbol: string, quantity: number, indexPrices?: [number, number]): Promise<number> {
|
|
580
594
|
if (this.proxyContract == null) {
|
|
581
595
|
throw Error("no proxy contract initialized. Use createProxyInstance().");
|
|
582
596
|
}
|
|
583
|
-
if (indexPrices==undefined) {
|
|
597
|
+
if (indexPrices == undefined) {
|
|
584
598
|
// fetch from API
|
|
585
599
|
let obj = await this.priceFeedGetter.fetchPricesForPerpetual(symbol);
|
|
586
600
|
indexPrices = [obj.idxPrices[0], obj.idxPrices[1]];
|
|
@@ -600,11 +614,14 @@ export default class MarketData extends PerpetualDataHandler {
|
|
|
600
614
|
* @param indexPrices S2 and S3 prices/isMarketOpen if not provided fetch via REST API
|
|
601
615
|
* @returns PerpetualState reference
|
|
602
616
|
*/
|
|
603
|
-
public async getPerpetualState(
|
|
617
|
+
public async getPerpetualState(
|
|
618
|
+
symbol: string,
|
|
619
|
+
indexPriceInfo?: [number, number, boolean, boolean]
|
|
620
|
+
): Promise<PerpetualState> {
|
|
604
621
|
if (this.proxyContract == null) {
|
|
605
622
|
throw Error("no proxy contract initialized. Use createProxyInstance().");
|
|
606
623
|
}
|
|
607
|
-
if (indexPriceInfo==undefined) {
|
|
624
|
+
if (indexPriceInfo == undefined) {
|
|
608
625
|
let obj = await this.priceFeedGetter.fetchPricesForPerpetual(symbol);
|
|
609
626
|
indexPriceInfo = [obj.idxPrices[0], obj.idxPrices[1], obj.mktClosed[0], obj.mktClosed[1]];
|
|
610
627
|
}
|
|
@@ -706,7 +723,7 @@ export default class MarketData extends PerpetualDataHandler {
|
|
|
706
723
|
return digests;
|
|
707
724
|
}
|
|
708
725
|
|
|
709
|
-
public async getAvailableMargin(traderAddr: string, symbol: string): Promise<number> {
|
|
726
|
+
public async getAvailableMargin(traderAddr: string, symbol: string, indexPrices?: [number, number]): Promise<number> {
|
|
710
727
|
if (this.proxyContract == null) {
|
|
711
728
|
throw Error("no proxy contract initialized. Use createProxyInstance().");
|
|
712
729
|
}
|
|
@@ -716,8 +733,16 @@ export default class MarketData extends PerpetualDataHandler {
|
|
|
716
733
|
this.symbolToPerpStaticInfo,
|
|
717
734
|
this.proxyContract
|
|
718
735
|
);
|
|
736
|
+
if (indexPrices == undefined) {
|
|
737
|
+
// fetch from API
|
|
738
|
+
let obj = await this.priceFeedGetter.fetchPricesForPerpetual(symbol);
|
|
739
|
+
indexPrices = [obj.idxPrices[0], obj.idxPrices[1]];
|
|
740
|
+
}
|
|
741
|
+
let S2 = indexPrices[0];
|
|
742
|
+
let ccyType = this.getPerpetualStaticInfo(symbol).collateralCurrencyType;
|
|
743
|
+
let S3 = ccyType == COLLATERAL_CURRENCY_QUANTO ? indexPrices[1] : ccyType == COLLATERAL_CURRENCY_QUOTE ? 1 : S2;
|
|
719
744
|
let perpInfo = this.symbolToPerpStaticInfo.get(symbol);
|
|
720
|
-
let balanceCC = mgnAcct.collateralCC + mgnAcct.unrealizedPnlQuoteCCY /
|
|
745
|
+
let balanceCC = mgnAcct.collateralCC + mgnAcct.unrealizedPnlQuoteCCY / S3;
|
|
721
746
|
let initalMarginCC = Math.abs(
|
|
722
747
|
(perpInfo!.initialMarginRate * mgnAcct.positionNotionalBaseCCY * mgnAcct.markPrice) /
|
|
723
748
|
mgnAcct.collToQuoteConversion
|
|
@@ -728,7 +753,7 @@ export default class MarketData extends PerpetualDataHandler {
|
|
|
728
753
|
public static async _exchangeInfo(
|
|
729
754
|
_proxyContract: ethers.Contract,
|
|
730
755
|
_poolStaticInfos: Array<PoolStaticInfo>,
|
|
731
|
-
_symbolToPerpStaticInfo
|
|
756
|
+
_symbolToPerpStaticInfo: Map<string, PerpetualStaticInfo>,
|
|
732
757
|
_symbolList: Map<string, string>,
|
|
733
758
|
_priceFeedGetter: PriceFeeds
|
|
734
759
|
): Promise<ExchangeInfo> {
|
|
@@ -736,18 +761,18 @@ export default class MarketData extends PerpetualDataHandler {
|
|
|
736
761
|
let factory = await _proxyContract.getOracleFactory();
|
|
737
762
|
let info: ExchangeInfo = { pools: [], oracleFactoryAddr: factory, proxyAddr: _proxyContract.address };
|
|
738
763
|
const numPools = nestedPerpetualIDs.length;
|
|
739
|
-
|
|
764
|
+
|
|
740
765
|
// get all prices
|
|
741
766
|
let allSym = new Set<string>();
|
|
742
|
-
for(let perpSymbol of _symbolToPerpStaticInfo.keys()) {
|
|
743
|
-
let sInfo
|
|
767
|
+
for (let perpSymbol of _symbolToPerpStaticInfo.keys()) {
|
|
768
|
+
let sInfo: PerpetualStaticInfo | undefined = _symbolToPerpStaticInfo.get(perpSymbol);
|
|
744
769
|
allSym.add(sInfo!.S2Symbol);
|
|
745
|
-
if(sInfo!.S3Symbol!=
|
|
770
|
+
if (sInfo!.S3Symbol != "") {
|
|
746
771
|
allSym.add(sInfo!.S3Symbol);
|
|
747
772
|
}
|
|
748
773
|
}
|
|
749
774
|
let allSymArr = Array.from(allSym.values());
|
|
750
|
-
let idxPriceMap
|
|
775
|
+
let idxPriceMap: Map<string, [number, boolean]> = await _priceFeedGetter.fetchPrices(allSymArr);
|
|
751
776
|
|
|
752
777
|
for (var j = 0; j < numPools; j++) {
|
|
753
778
|
let perpetualIDs = nestedPerpetualIDs[j];
|
|
@@ -767,33 +792,39 @@ export default class MarketData extends PerpetualDataHandler {
|
|
|
767
792
|
let poolSymbol = PoolState.poolSymbol;
|
|
768
793
|
for (var k = 0; k < perpetualIDs.length; k++) {
|
|
769
794
|
let perp = await _proxyContract.getPerpetual(perpetualIDs[k]);
|
|
770
|
-
let symS2 =
|
|
771
|
-
|
|
772
|
-
|
|
795
|
+
let symS2 =
|
|
796
|
+
contractSymbolToSymbol(perp.S2BaseCCY, _symbolList) +
|
|
797
|
+
"-" +
|
|
798
|
+
contractSymbolToSymbol(perp.S2QuoteCCY, _symbolList);
|
|
799
|
+
let symS3 =
|
|
800
|
+
contractSymbolToSymbol(perp.S3BaseCCY, _symbolList) +
|
|
801
|
+
"-" +
|
|
802
|
+
contractSymbolToSymbol(perp.S3QuoteCCY, _symbolList);
|
|
803
|
+
let perpSymbol = symS2 + "-" + poolSymbol;
|
|
773
804
|
//console.log("perpsymbol=",perpSymbol);
|
|
774
|
-
let res = idxPriceMap.get(symS2)
|
|
775
|
-
if (res==undefined) {
|
|
805
|
+
let res = idxPriceMap.get(symS2);
|
|
806
|
+
if (res == undefined) {
|
|
776
807
|
throw new Error(`Price for index ${symS2} could not be fetched - config issue`);
|
|
777
808
|
}
|
|
778
|
-
let [indexS2, isS2MktClosed]
|
|
809
|
+
let [indexS2, isS2MktClosed]: [number, boolean] = [res[0], res[1]];
|
|
779
810
|
let indexS3 = 1;
|
|
780
811
|
let isS3MktClosed = false;
|
|
781
812
|
if (perp.eCollateralCurrency == COLLATERAL_CURRENCY_BASE) {
|
|
782
813
|
indexS3 = indexS2;
|
|
783
814
|
} else if (perp.eCollateralCurrency == COLLATERAL_CURRENCY_QUANTO) {
|
|
784
815
|
res = idxPriceMap.get(symS3);
|
|
785
|
-
if (res==undefined) {
|
|
816
|
+
if (res == undefined) {
|
|
786
817
|
throw new Error(`Price for index ${symS3} could not be fetched - config issue`);
|
|
787
818
|
} else {
|
|
788
819
|
indexS3 = res[0];
|
|
789
|
-
isS3MktClosed = res[1];
|
|
820
|
+
isS3MktClosed = res[1];
|
|
790
821
|
}
|
|
791
822
|
}
|
|
792
823
|
let fMidPrice = await _proxyContract.queryPerpetualPrice(perpetualIDs[k], BigNumber.from(0), [
|
|
793
824
|
floatToABK64x64(indexS2),
|
|
794
|
-
floatToABK64x64(indexS3)
|
|
825
|
+
floatToABK64x64(indexS3),
|
|
795
826
|
]);
|
|
796
|
-
|
|
827
|
+
|
|
797
828
|
let markPremiumRate = ABK64x64ToFloat(perp.currentMarkPremiumRate.fPrice);
|
|
798
829
|
let currentFundingRateBps = 1e4 * ABK64x64ToFloat(perp.fCurrentFundingRate);
|
|
799
830
|
let state = PERP_STATE_STR[perp.state];
|
|
@@ -809,7 +840,7 @@ export default class MarketData extends PerpetualDataHandler {
|
|
|
809
840
|
currentFundingRateBps: currentFundingRateBps,
|
|
810
841
|
openInterestBC: ABK64x64ToFloat(perp.fOpenInterest),
|
|
811
842
|
maxPositionBC: Infinity,
|
|
812
|
-
isMarketClosed: isS2MktClosed || isS3MktClosed
|
|
843
|
+
isMarketClosed: isS2MktClosed || isS3MktClosed,
|
|
813
844
|
};
|
|
814
845
|
PoolState.perpetuals.push(PerpetualState);
|
|
815
846
|
}
|
|
@@ -166,21 +166,20 @@ export default class PerpetualDataHandler {
|
|
|
166
166
|
// try to find a limit order book
|
|
167
167
|
let lobAddr = await this.lobFactoryContract.getOrderBookAddress(perpetualIDs[k]);
|
|
168
168
|
currentLimitOrderBookAddr.push(lobAddr);
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}
|
|
169
|
+
|
|
170
|
+
// we find out the pool currency by looking at all perpetuals
|
|
171
|
+
// unless for quanto perpetuals, we know the pool currency
|
|
172
|
+
// from the perpetual. This fails if we have a pool with only
|
|
173
|
+
// quanto perpetuals
|
|
174
|
+
if (perp.eCollateralCurrency == COLLATERAL_CURRENCY_BASE) {
|
|
175
|
+
poolCCY = poolCCY ?? base;
|
|
176
|
+
ccy.push(CollaterlCCY.BASE);
|
|
177
|
+
} else if (perp.eCollateralCurrency == COLLATERAL_CURRENCY_QUOTE) {
|
|
178
|
+
poolCCY = poolCCY ?? quote;
|
|
179
|
+
ccy.push(CollaterlCCY.QUOTE);
|
|
180
|
+
} else {
|
|
181
|
+
poolCCY = poolCCY ?? base3;
|
|
182
|
+
ccy.push(CollaterlCCY.QUANTO);
|
|
184
183
|
}
|
|
185
184
|
}
|
|
186
185
|
if (perpetualIDs.length == 0) {
|
|
@@ -352,12 +351,17 @@ export default class PerpetualDataHandler {
|
|
|
352
351
|
traderAddr: string,
|
|
353
352
|
symbol: string,
|
|
354
353
|
symbolToPerpStaticInfo: Map<string, PerpetualStaticInfo>,
|
|
355
|
-
_proxyContract: ethers.Contract
|
|
354
|
+
_proxyContract: ethers.Contract,
|
|
355
|
+
_pxS2S3?: [number, number | undefined]
|
|
356
356
|
): Promise<MarginAccount> {
|
|
357
357
|
let perpId = Number(symbol);
|
|
358
358
|
if (isNaN(perpId)) {
|
|
359
359
|
perpId = PerpetualDataHandler.symbolToPerpetualId(symbol, symbolToPerpStaticInfo);
|
|
360
360
|
}
|
|
361
|
+
// TODO: correct numbers using actual S2, S3
|
|
362
|
+
// if (_pxS2S3 == undefined) {
|
|
363
|
+
// // fetch s2,s3
|
|
364
|
+
// }
|
|
361
365
|
const idx_cash = 3;
|
|
362
366
|
const idx_notional = 4;
|
|
363
367
|
const idx_locked_in = 5;
|
|
@@ -434,20 +438,23 @@ export default class PerpetualDataHandler {
|
|
|
434
438
|
indexPrices: [number, number, boolean, boolean]
|
|
435
439
|
): Promise<PerpetualState> {
|
|
436
440
|
let perpId = PerpetualDataHandler.symbolToPerpetualId(symbol, symbolToPerpStaticInfo);
|
|
441
|
+
let staticInfo = symbolToPerpStaticInfo.get(symbol)!;
|
|
437
442
|
let ccy = symbol.split("-");
|
|
438
443
|
let [S2, S3] = [indexPrices[0], indexPrices[1]];
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
+
if (staticInfo.collateralCurrencyType == CollaterlCCY.BASE) {
|
|
445
|
+
S3 = S2;
|
|
446
|
+
} else if (staticInfo.collateralCurrencyType == CollaterlCCY.QUOTE) {
|
|
447
|
+
S3 = 1;
|
|
448
|
+
}
|
|
449
|
+
let ammState = await _proxyContract.getAMMState(perpId, [S2, S3].map(floatToABK64x64));
|
|
450
|
+
let markPrice = S2 * (1 + ABK64x64ToFloat(ammState[8]));
|
|
444
451
|
let state = {
|
|
445
452
|
id: perpId,
|
|
446
453
|
state: PERP_STATE_STR[ammState[13]],
|
|
447
454
|
baseCurrency: ccy[0],
|
|
448
455
|
quoteCurrency: ccy[1],
|
|
449
|
-
indexPrice:
|
|
450
|
-
collToQuoteIndexPrice:
|
|
456
|
+
indexPrice: S2,
|
|
457
|
+
collToQuoteIndexPrice: S3,
|
|
451
458
|
markPrice: markPrice,
|
|
452
459
|
midPrice: ABK64x64ToFloat(ammState[10]),
|
|
453
460
|
currentFundingRateBps: ABK64x64ToFloat(ammState[14]) * 1e4,
|
|
@@ -455,11 +462,6 @@ export default class PerpetualDataHandler {
|
|
|
455
462
|
maxPositionBC: ABK64x64ToFloat(ammState[12]),
|
|
456
463
|
isMarketClosed: indexPrices[2] || indexPrices[3],
|
|
457
464
|
};
|
|
458
|
-
if (symbolToPerpStaticInfo.get(symbol)?.collateralCurrencyType == CollaterlCCY.BASE) {
|
|
459
|
-
state.collToQuoteIndexPrice = state.indexPrice;
|
|
460
|
-
} else if (symbolToPerpStaticInfo.get(symbol)?.collateralCurrencyType == CollaterlCCY.QUOTE) {
|
|
461
|
-
state.collToQuoteIndexPrice = 1;
|
|
462
|
-
}
|
|
463
465
|
return state;
|
|
464
466
|
}
|
|
465
467
|
|
|
@@ -834,4 +836,50 @@ export default class PerpetualDataHandler {
|
|
|
834
836
|
}
|
|
835
837
|
return undefined;
|
|
836
838
|
}
|
|
839
|
+
|
|
840
|
+
/**
|
|
841
|
+
* Performs basic validity checks on a given order
|
|
842
|
+
* @param order Order struct
|
|
843
|
+
* @param traderAccount Trader account
|
|
844
|
+
* @param perpStaticInfo Symbol to perpetual info map
|
|
845
|
+
*/
|
|
846
|
+
protected static checkOrder(
|
|
847
|
+
order: Order,
|
|
848
|
+
traderAccount: MarginAccount,
|
|
849
|
+
perpStaticInfo: Map<string, PerpetualStaticInfo>
|
|
850
|
+
) {
|
|
851
|
+
// this throws error if not found
|
|
852
|
+
let perpetualId = PerpetualDataHandler.symbolToPerpetualId(order.symbol, perpStaticInfo);
|
|
853
|
+
|
|
854
|
+
// check side
|
|
855
|
+
if (order.side != BUY_SIDE && order.side != SELL_SIDE) {
|
|
856
|
+
throw Error(`order side must be ${BUY_SIDE} or ${SELL_SIDE}`);
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
// check amount
|
|
860
|
+
let lotSize = perpStaticInfo.get(order.symbol)!.lotSizeBC;
|
|
861
|
+
let curPos =
|
|
862
|
+
traderAccount.side == CLOSED_SIDE
|
|
863
|
+
? 0
|
|
864
|
+
: (traderAccount.side == BUY_SIDE ? 1 : -1) * traderAccount.positionNotionalBaseCCY;
|
|
865
|
+
let newPos = curPos + (order.side == BUY_SIDE ? 1 : -1) * order.quantity;
|
|
866
|
+
if (Math.abs(order.quantity) < lotSize || (Math.abs(newPos) >= lotSize && Math.abs(newPos) < 10 * lotSize)) {
|
|
867
|
+
throw Error(`trade amount too small: ${order.quantity} ${perpStaticInfo.get(order.symbol)!.S2Symbol}`);
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
// check limit price
|
|
871
|
+
if (order.side == BUY_SIDE && order.limitPrice != undefined && order.limitPrice <= 0) {
|
|
872
|
+
throw Error(`invalid limit price for buy order: ${order.limitPrice}`);
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
// broker fee
|
|
876
|
+
if (order.brokerFeeTbps != undefined && order.brokerFeeTbps < 0) {
|
|
877
|
+
throw Error(`invalid broker fee: ${order.brokerFeeTbps / 10} bps`);
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
// stop price
|
|
881
|
+
if (order.stopPrice != undefined && order.stopPrice < 0) {
|
|
882
|
+
throw Error(`invalid stop price: ${order.stopPrice}`);
|
|
883
|
+
}
|
|
884
|
+
}
|
|
837
885
|
}
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const D8X_SDK_VERSION = "0.1.
|
|
1
|
+
export const D8X_SDK_VERSION = "0.1.2";
|