@d8x/perpetuals-sdk 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -1
- package/abi/Multicall.json +86 -0
- package/abi/central-park/IPerpetualManager.json +7 -2
- package/abi/central-park/LimitOrderBook.json +130 -2
- package/config/defaultConfig.json +2 -2
- package/dist/liquidatorTool.d.ts +2 -1
- package/dist/liquidatorTool.js +8 -2
- package/dist/marketData.d.ts +8 -4
- package/dist/marketData.js +178 -97
- package/dist/perpetualDataHandler.d.ts +3 -2
- package/dist/perpetualDataHandler.js +5 -8
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/src/liquidatorTool.ts +8 -2
- package/src/marketData.ts +225 -166
- package/src/perpetualDataHandler.ts +6 -7
- package/src/version.ts +1 -1
package/src/marketData.ts
CHANGED
|
@@ -212,11 +212,13 @@ export default class MarketData extends PerpetualDataHandler {
|
|
|
212
212
|
if (this.proxyContract == null) {
|
|
213
213
|
throw Error("no proxy contract initialized. Use createProxyInstance().");
|
|
214
214
|
}
|
|
215
|
+
let obj = await this.priceFeedGetter.fetchPricesForPerpetual(symbol);
|
|
215
216
|
let mgnAcct = await PerpetualDataHandler.getMarginAccount(
|
|
216
217
|
traderAddr,
|
|
217
218
|
symbol,
|
|
218
219
|
this.symbolToPerpStaticInfo,
|
|
219
|
-
this.proxyContract
|
|
220
|
+
this.proxyContract,
|
|
221
|
+
[obj.idxPrices[0], obj.idxPrices[1]]
|
|
220
222
|
);
|
|
221
223
|
return mgnAcct;
|
|
222
224
|
}
|
|
@@ -225,48 +227,149 @@ export default class MarketData extends PerpetualDataHandler {
|
|
|
225
227
|
* Estimates what the position risk will be if a given order is executed.
|
|
226
228
|
* @param traderAddr Address of trader
|
|
227
229
|
* @param order Order to be submitted
|
|
228
|
-
* @param
|
|
230
|
+
* @param account Position risk before trade
|
|
231
|
+
* @param indexPriceInfo Index prices and market status (open/closed)
|
|
229
232
|
* @returns {MarginAccount} Position risk after trade
|
|
230
233
|
*/
|
|
231
234
|
public async positionRiskOnTrade(
|
|
232
235
|
traderAddr: string,
|
|
233
236
|
order: Order,
|
|
234
|
-
|
|
237
|
+
account?: MarginAccount,
|
|
235
238
|
indexPriceInfo?: [number, number, boolean, boolean]
|
|
236
|
-
): Promise<MarginAccount> {
|
|
239
|
+
): Promise<{ newPositionRisk: MarginAccount; orderCost: number }> {
|
|
237
240
|
if (this.proxyContract == null) {
|
|
238
241
|
throw Error("no proxy contract initialized. Use createProxyInstance().");
|
|
239
242
|
}
|
|
240
|
-
|
|
241
|
-
|
|
243
|
+
// fetch undefined data
|
|
244
|
+
if (account == undefined) {
|
|
245
|
+
account = await this.positionRisk(traderAddr, order.symbol);
|
|
242
246
|
}
|
|
243
247
|
if (indexPriceInfo == undefined) {
|
|
244
|
-
let obj = await this.priceFeedGetter.fetchPricesForPerpetual(
|
|
248
|
+
let obj = await this.priceFeedGetter.fetchPricesForPerpetual(account.symbol);
|
|
245
249
|
indexPriceInfo = [obj.idxPrices[0], obj.idxPrices[1], obj.mktClosed[0], obj.mktClosed[1]];
|
|
246
250
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
251
|
+
|
|
252
|
+
let lotSizeBC = MarketData._getLotSize(account.symbol, this.symbolToPerpStaticInfo);
|
|
253
|
+
// Too small, no change to account
|
|
254
|
+
if (Math.abs(order.quantity) < lotSizeBC) {
|
|
255
|
+
return { newPositionRisk: account, orderCost: 0 };
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Current state:
|
|
259
|
+
// perp (for FXs and such)
|
|
256
260
|
let perpetualState = await this.getPerpetualState(order.symbol, indexPriceInfo);
|
|
261
|
+
let [S2, S3, Sm] = [perpetualState.indexPrice, perpetualState.collToQuoteIndexPrice, perpetualState.markPrice];
|
|
262
|
+
// cash in margin account: upon trading, unpaid funding will be realized
|
|
263
|
+
let currentMarginCashCC = account.collateralCC;
|
|
264
|
+
// signed position, still correct if side is closed (==0)
|
|
265
|
+
let currentPositionBC = (account.side == BUY_SIDE ? 1 : -1) * account.positionNotionalBaseCCY;
|
|
266
|
+
// signed locked-in value
|
|
267
|
+
let currentLockedInQC = account.entryPrice * currentPositionBC;
|
|
268
|
+
|
|
269
|
+
// New trader state:
|
|
270
|
+
// signed trade amount
|
|
271
|
+
let tradeAmountBC = Math.abs(order.quantity) * (order.side == BUY_SIDE ? 1 : -1);
|
|
272
|
+
// signed position
|
|
273
|
+
let newPositionBC = currentPositionBC + tradeAmountBC;
|
|
274
|
+
if (Math.abs(newPositionBC) < 10 * lotSizeBC) {
|
|
275
|
+
// fully closed
|
|
276
|
+
tradeAmountBC = -currentPositionBC;
|
|
277
|
+
newPositionBC = 0;
|
|
278
|
+
}
|
|
279
|
+
let newSide = newPositionBC > 0 ? BUY_SIDE : newPositionBC < 0 ? SELL_SIDE : CLOSED_SIDE;
|
|
257
280
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
281
|
+
// price for this order = limit price (conservative) if given, else the current perp price
|
|
282
|
+
let tradePrice = order.limitPrice ?? (await this.getPerpetualPrice(order.symbol, tradeAmountBC));
|
|
283
|
+
|
|
284
|
+
// fees
|
|
285
|
+
let poolId = PerpetualDataHandler._getPoolIdFromSymbol(order.symbol, this.poolStaticInfos);
|
|
286
|
+
let exchangeFeeTbps = await this.proxyContract.queryExchangeFee(
|
|
287
|
+
poolId,
|
|
288
|
+
traderAddr,
|
|
289
|
+
order.brokerAddr ?? ZERO_ADDRESS
|
|
290
|
+
);
|
|
291
|
+
let exchangeFeeCC = (Math.abs(tradeAmountBC) * exchangeFeeTbps * 1e-5 * S2) / S3;
|
|
292
|
+
let brokerFeeCC = (Math.abs(tradeAmountBC) * (order.brokerFeeTbps ?? 0) * 1e-5 * S2) / S3;
|
|
293
|
+
|
|
294
|
+
// Trade type:
|
|
295
|
+
let isClose = newPositionBC == 0 || newPositionBC * tradeAmountBC < 0;
|
|
296
|
+
let isOpen = newPositionBC != 0 && (currentPositionBC == 0 || newPositionBC * currentPositionBC > 0); // regular open, no flip
|
|
297
|
+
let isFlip = Math.abs(newPositionBC) > Math.abs(currentPositionBC) && !isOpen; // flip position sign, not fully closed
|
|
298
|
+
let keepPositionLvgOnClose = (order.keepPositionLvg ?? false) && !isOpen;
|
|
299
|
+
|
|
300
|
+
// Contract: _doMarginCollateralActions
|
|
301
|
+
// No collateral actions if
|
|
302
|
+
// 1) leverage is not set or
|
|
303
|
+
// 2) fully closed after trade or
|
|
304
|
+
// 3) is a partial closing, it doesn't flip, and keep lvg flag is not set
|
|
305
|
+
let traderDepositCC: number;
|
|
306
|
+
let targetLvg: number;
|
|
307
|
+
if (order.leverage == undefined || newPositionBC == 0 || (!isOpen && !isFlip && !keepPositionLvgOnClose)) {
|
|
308
|
+
traderDepositCC = 0;
|
|
309
|
+
targetLvg = 0;
|
|
310
|
+
} else {
|
|
311
|
+
// 1) opening and flipping trades need to specify a leverage: default to max if not given
|
|
312
|
+
// 2) for others it's ignored, set target to 0
|
|
313
|
+
let initialMarginRate = this.symbolToPerpStaticInfo.get(account.symbol)!.initialMarginRate;
|
|
314
|
+
targetLvg = isFlip || isOpen ? order.leverage ?? 1 / initialMarginRate : 0;
|
|
315
|
+
let [b0, pos0] = isOpen ? [0, 0] : [account.collateralCC, currentPositionBC];
|
|
316
|
+
traderDepositCC = getDepositAmountForLvgTrade(b0, pos0, tradeAmountBC, targetLvg, tradePrice, S3, Sm);
|
|
317
|
+
console.log("deposit for trget lvg:", traderDepositCC);
|
|
318
|
+
// fees are paid from wallet in this case
|
|
319
|
+
// referral rebate??
|
|
320
|
+
traderDepositCC += exchangeFeeCC + brokerFeeCC;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Contract: _executeTrade
|
|
324
|
+
let deltaCashCC = (-tradeAmountBC * (tradePrice - S2)) / S3;
|
|
325
|
+
let deltaLockedQC = tradeAmountBC * S2;
|
|
326
|
+
if (isClose) {
|
|
327
|
+
let pnl = account.entryPrice * tradeAmountBC - deltaLockedQC;
|
|
328
|
+
deltaLockedQC += pnl;
|
|
329
|
+
deltaCashCC += pnl / S3;
|
|
330
|
+
}
|
|
331
|
+
// funding and fees
|
|
332
|
+
deltaCashCC = deltaCashCC + account.unrealizedFundingCollateralCCY - exchangeFeeCC - brokerFeeCC;
|
|
333
|
+
|
|
334
|
+
// New cash, locked-in, entry price & leverage after trade
|
|
335
|
+
let newLockedInValueQC = currentLockedInQC + deltaLockedQC;
|
|
336
|
+
let newMarginCashCC = currentMarginCashCC + deltaCashCC + traderDepositCC;
|
|
337
|
+
let newEntryPrice = newPositionBC == 0 ? 0 : Math.abs(newLockedInValueQC / newPositionBC);
|
|
338
|
+
let newMarginBalanceCC = newMarginCashCC + (newPositionBC * Sm - newLockedInValueQC) / S3;
|
|
339
|
+
let newLeverage =
|
|
340
|
+
newPositionBC == 0
|
|
341
|
+
? 0
|
|
342
|
+
: newMarginBalanceCC <= 0
|
|
343
|
+
? Infinity
|
|
344
|
+
: (Math.abs(newPositionBC) * Sm) / S3 / newMarginBalanceCC;
|
|
345
|
+
|
|
346
|
+
// Liquidation params
|
|
347
|
+
let [S2Liq, S3Liq, tau] = MarketData._getLiquidationParams(
|
|
348
|
+
account.symbol,
|
|
349
|
+
newLockedInValueQC,
|
|
350
|
+
newPositionBC,
|
|
351
|
+
newMarginCashCC,
|
|
352
|
+
Sm,
|
|
353
|
+
S3,
|
|
268
354
|
this.symbolToPerpStaticInfo
|
|
269
355
|
);
|
|
356
|
+
|
|
357
|
+
// New position risk
|
|
358
|
+
let newPositionRisk: MarginAccount = {
|
|
359
|
+
symbol: account.symbol,
|
|
360
|
+
positionNotionalBaseCCY: Math.abs(newPositionBC),
|
|
361
|
+
side: newSide,
|
|
362
|
+
entryPrice: newEntryPrice,
|
|
363
|
+
leverage: newLeverage,
|
|
364
|
+
markPrice: Sm,
|
|
365
|
+
unrealizedPnlQuoteCCY: newPositionBC * Sm - newLockedInValueQC,
|
|
366
|
+
unrealizedFundingCollateralCCY: 0,
|
|
367
|
+
collateralCC: newMarginCashCC,
|
|
368
|
+
collToQuoteConversion: S3,
|
|
369
|
+
liquidationPrice: [S2Liq, S3Liq],
|
|
370
|
+
liquidationLvg: 1 / tau,
|
|
371
|
+
};
|
|
372
|
+
return { newPositionRisk: newPositionRisk, orderCost: traderDepositCC };
|
|
270
373
|
}
|
|
271
374
|
|
|
272
375
|
/**
|
|
@@ -278,167 +381,121 @@ export default class MarketData extends PerpetualDataHandler {
|
|
|
278
381
|
*/
|
|
279
382
|
public async positionRiskOnCollateralAction(
|
|
280
383
|
deltaCollateral: number,
|
|
281
|
-
|
|
384
|
+
account: MarginAccount,
|
|
282
385
|
indexPriceInfo?: [number, number, boolean, boolean]
|
|
283
386
|
): Promise<MarginAccount> {
|
|
284
387
|
if (this.proxyContract == null) {
|
|
285
388
|
throw Error("no proxy contract initialized. Use createProxyInstance().");
|
|
286
389
|
}
|
|
390
|
+
if (deltaCollateral + account.collateralCC + account.unrealizedFundingCollateralCCY < 0) {
|
|
391
|
+
throw Error("not enough margin to remove");
|
|
392
|
+
}
|
|
287
393
|
if (indexPriceInfo == undefined) {
|
|
288
|
-
let obj = await this.priceFeedGetter.fetchPricesForPerpetual(
|
|
394
|
+
let obj = await this.priceFeedGetter.fetchPricesForPerpetual(account.symbol);
|
|
289
395
|
indexPriceInfo = [obj.idxPrices[0], obj.idxPrices[1], obj.mktClosed[0], obj.mktClosed[1]];
|
|
290
396
|
}
|
|
291
|
-
let perpetualState = await this.getPerpetualState(
|
|
397
|
+
let perpetualState = await this.getPerpetualState(account.symbol, indexPriceInfo);
|
|
398
|
+
let [S2, S3, Sm] = [perpetualState.indexPrice, perpetualState.collToQuoteIndexPrice, perpetualState.markPrice];
|
|
399
|
+
|
|
400
|
+
// no position: just increase collateral and kill liquidation vars
|
|
401
|
+
if (account.positionNotionalBaseCCY == 0) {
|
|
402
|
+
return {
|
|
403
|
+
symbol: account.symbol,
|
|
404
|
+
positionNotionalBaseCCY: account.positionNotionalBaseCCY,
|
|
405
|
+
side: account.side,
|
|
406
|
+
entryPrice: account.entryPrice,
|
|
407
|
+
leverage: account.leverage,
|
|
408
|
+
markPrice: Sm,
|
|
409
|
+
unrealizedPnlQuoteCCY: account.unrealizedPnlQuoteCCY,
|
|
410
|
+
unrealizedFundingCollateralCCY: account.unrealizedFundingCollateralCCY,
|
|
411
|
+
collateralCC: account.collateralCC + deltaCollateral,
|
|
412
|
+
collToQuoteConversion: S3,
|
|
413
|
+
liquidationPrice: [0, undefined],
|
|
414
|
+
liquidationLvg: Infinity,
|
|
415
|
+
};
|
|
416
|
+
}
|
|
292
417
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
418
|
+
let positionBC = account.positionNotionalBaseCCY * (account.side == BUY_SIDE ? 1 : -1);
|
|
419
|
+
let lockedInQC = account.entryPrice * positionBC;
|
|
420
|
+
let newMarginCashCC = account.collateralCC + deltaCollateral;
|
|
421
|
+
let newMarginBalanceCC =
|
|
422
|
+
newMarginCashCC + account.unrealizedFundingCollateralCCY + (positionBC * Sm - lockedInQC) / S3;
|
|
423
|
+
if (newMarginBalanceCC <= 0) {
|
|
424
|
+
return {
|
|
425
|
+
symbol: account.symbol,
|
|
426
|
+
positionNotionalBaseCCY: account.positionNotionalBaseCCY,
|
|
427
|
+
side: account.side,
|
|
428
|
+
entryPrice: account.entryPrice,
|
|
429
|
+
leverage: Infinity,
|
|
430
|
+
markPrice: Sm,
|
|
431
|
+
unrealizedPnlQuoteCCY: account.unrealizedPnlQuoteCCY,
|
|
432
|
+
unrealizedFundingCollateralCCY: account.unrealizedFundingCollateralCCY,
|
|
433
|
+
collateralCC: newMarginCashCC,
|
|
434
|
+
collToQuoteConversion: S3,
|
|
435
|
+
liquidationPrice: [S2, S3],
|
|
436
|
+
liquidationLvg: 0,
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
let newLeverage = (Math.abs(positionBC) * Sm) / S3 / newMarginBalanceCC;
|
|
440
|
+
|
|
441
|
+
// Liquidation params
|
|
442
|
+
let [S2Liq, S3Liq, tau] = MarketData._getLiquidationParams(
|
|
443
|
+
account.symbol,
|
|
444
|
+
lockedInQC,
|
|
445
|
+
positionBC,
|
|
446
|
+
newMarginCashCC,
|
|
447
|
+
Sm,
|
|
448
|
+
S3,
|
|
303
449
|
this.symbolToPerpStaticInfo
|
|
304
450
|
);
|
|
451
|
+
|
|
452
|
+
// New position risk
|
|
453
|
+
let newPositionRisk: MarginAccount = {
|
|
454
|
+
symbol: account.symbol,
|
|
455
|
+
positionNotionalBaseCCY: account.positionNotionalBaseCCY,
|
|
456
|
+
side: account.side,
|
|
457
|
+
entryPrice: account.entryPrice,
|
|
458
|
+
leverage: newLeverage,
|
|
459
|
+
markPrice: Sm,
|
|
460
|
+
unrealizedPnlQuoteCCY: account.unrealizedPnlQuoteCCY,
|
|
461
|
+
unrealizedFundingCollateralCCY: account.unrealizedFundingCollateralCCY,
|
|
462
|
+
collateralCC: newMarginCashCC,
|
|
463
|
+
collToQuoteConversion: S3,
|
|
464
|
+
liquidationPrice: [S2Liq, S3Liq],
|
|
465
|
+
liquidationLvg: 1 / tau,
|
|
466
|
+
};
|
|
467
|
+
return newPositionRisk;
|
|
305
468
|
}
|
|
306
469
|
|
|
307
|
-
protected static
|
|
470
|
+
protected static _getLiquidationParams(
|
|
308
471
|
symbol: string,
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
feeRate: number,
|
|
315
|
-
perpetualState: PerpetualState,
|
|
316
|
-
currentPositionRisk: MarginAccount,
|
|
472
|
+
lockedInQC: number,
|
|
473
|
+
signedPositionBC: number,
|
|
474
|
+
marginCashCC: number,
|
|
475
|
+
markPrice: number,
|
|
476
|
+
collToQuoteConversion: number,
|
|
317
477
|
symbolToPerpStaticInfo: Map<string, PerpetualStaticInfo>
|
|
318
|
-
):
|
|
319
|
-
let currentSide = currentPositionRisk.side;
|
|
320
|
-
let currentPosition = (currentSide == BUY_SIDE ? 1 : -1) * currentPositionRisk.positionNotionalBaseCCY;
|
|
321
|
-
let newPosition = currentPosition + tradeAmount;
|
|
322
|
-
let newSide = newPosition > 0 ? BUY_SIDE : newPosition < 0 ? SELL_SIDE : CLOSED_SIDE;
|
|
323
|
-
let lockedInValue = currentPositionRisk.entryPrice * currentPosition;
|
|
324
|
-
let newLockedInValue = lockedInValue + tradeAmount * tradePrice;
|
|
325
|
-
let newEntryPrice = newPosition == 0 ? 0 : Math.abs(newLockedInValue / newPosition);
|
|
326
|
-
if (tradeAmount == 0) {
|
|
327
|
-
keepPositionLvg = false;
|
|
328
|
-
}
|
|
329
|
-
let isOpen = newPosition != 0 && (tradeAmount == 0 || currentPosition == 0 || tradeAmount * currentPosition > 0);
|
|
330
|
-
let isFlip = Math.abs(tradeAmount) > Math.abs(currentPosition) && !isOpen;
|
|
331
|
-
let keepPositionLvgOnClose = keepPositionLvg && !isOpen;
|
|
332
|
-
// need these for leverage/margin calculations
|
|
333
|
-
let [markPrice, indexPriceS2, indexPriceS3] = [
|
|
334
|
-
perpetualState.markPrice,
|
|
335
|
-
perpetualState.indexPrice,
|
|
336
|
-
perpetualState.collToQuoteIndexPrice,
|
|
337
|
-
];
|
|
338
|
-
let newCollateral: number;
|
|
339
|
-
let newLeverage: number;
|
|
340
|
-
if (keepPositionLvg) {
|
|
341
|
-
// we have a target leverage for the resulting position
|
|
342
|
-
// this gives us the total margin needed in the account so that it satisfies the leverage condition
|
|
343
|
-
newCollateral = getMarginRequiredForLeveragedTrade(
|
|
344
|
-
currentPositionRisk.leverage,
|
|
345
|
-
currentPosition,
|
|
346
|
-
lockedInValue,
|
|
347
|
-
tradeAmount,
|
|
348
|
-
markPrice,
|
|
349
|
-
indexPriceS2,
|
|
350
|
-
indexPriceS3,
|
|
351
|
-
tradePrice,
|
|
352
|
-
feeRate
|
|
353
|
-
);
|
|
354
|
-
// the new leverage follows from the updated margin and position
|
|
355
|
-
newLeverage = getNewPositionLeverage(
|
|
356
|
-
tradeAmount,
|
|
357
|
-
newCollateral,
|
|
358
|
-
currentPosition,
|
|
359
|
-
lockedInValue,
|
|
360
|
-
tradePrice,
|
|
361
|
-
indexPriceS3,
|
|
362
|
-
markPrice
|
|
363
|
-
);
|
|
364
|
-
} else if (tradeAmount != 0) {
|
|
365
|
-
let deltaCash: number;
|
|
366
|
-
if (!isOpen && !isFlip && !keepPositionLvgOnClose) {
|
|
367
|
-
// cash comes from realized pnl
|
|
368
|
-
deltaCash = (tradeAmount * (tradePrice - currentPositionRisk.entryPrice)) / indexPriceS3;
|
|
369
|
-
newLockedInValue = lockedInValue + currentPositionRisk.entryPrice * tradeAmount;
|
|
370
|
-
} else {
|
|
371
|
-
// target lvg will default current lvg if not specified
|
|
372
|
-
let targetLvg = isFlip || isOpen ? tradeLeverage ?? 0 : 0;
|
|
373
|
-
let b0, pos0;
|
|
374
|
-
[b0, pos0] = isOpen ? [0, 0] : [currentPositionRisk.collateralCC, currentPosition];
|
|
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;
|
|
380
|
-
}
|
|
381
|
-
newCollateral = currentPositionRisk.collateralCC + deltaCash; // - (feeRate * Math.abs(tradeAmount) * indexPriceS2) / indexPriceS3;
|
|
382
|
-
// the new leverage corresponds to increasing the position and collateral according to the order
|
|
383
|
-
newLeverage = getNewPositionLeverage(
|
|
384
|
-
0,
|
|
385
|
-
newCollateral,
|
|
386
|
-
newPosition,
|
|
387
|
-
newLockedInValue,
|
|
388
|
-
tradePrice,
|
|
389
|
-
indexPriceS3,
|
|
390
|
-
markPrice
|
|
391
|
-
);
|
|
392
|
-
} else {
|
|
393
|
-
// there is no order, adding/removing collateral
|
|
394
|
-
newCollateral = currentPositionRisk.collateralCC + marginDeposit;
|
|
395
|
-
newLeverage = getNewPositionLeverage(
|
|
396
|
-
0,
|
|
397
|
-
newCollateral,
|
|
398
|
-
currentPosition,
|
|
399
|
-
lockedInValue,
|
|
400
|
-
indexPriceS2,
|
|
401
|
-
indexPriceS3,
|
|
402
|
-
markPrice
|
|
403
|
-
);
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
// liquidation vars
|
|
478
|
+
): [number, number | undefined, number] {
|
|
407
479
|
let S2Liq: number, S3Liq: number | undefined;
|
|
408
480
|
let tau = symbolToPerpStaticInfo.get(symbol)!.maintenanceMarginRate;
|
|
409
481
|
let ccyType = symbolToPerpStaticInfo.get(symbol)!.collateralCurrencyType;
|
|
410
482
|
if (ccyType == CollaterlCCY.BASE) {
|
|
411
|
-
S2Liq = calculateLiquidationPriceCollateralBase(
|
|
483
|
+
S2Liq = calculateLiquidationPriceCollateralBase(lockedInQC, signedPositionBC, marginCashCC, tau);
|
|
412
484
|
S3Liq = S2Liq;
|
|
413
485
|
} else if (ccyType == CollaterlCCY.QUANTO) {
|
|
414
|
-
S3Liq =
|
|
486
|
+
S3Liq = collToQuoteConversion;
|
|
415
487
|
S2Liq = calculateLiquidationPriceCollateralQuanto(
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
488
|
+
lockedInQC,
|
|
489
|
+
signedPositionBC,
|
|
490
|
+
marginCashCC,
|
|
419
491
|
tau,
|
|
420
|
-
|
|
492
|
+
collToQuoteConversion,
|
|
421
493
|
markPrice
|
|
422
494
|
);
|
|
423
495
|
} else {
|
|
424
|
-
S2Liq = calculateLiquidationPriceCollateralQuote(
|
|
496
|
+
S2Liq = calculateLiquidationPriceCollateralQuote(lockedInQC, signedPositionBC, marginCashCC, tau);
|
|
425
497
|
}
|
|
426
|
-
|
|
427
|
-
let newPositionRisk: MarginAccount = {
|
|
428
|
-
symbol: currentPositionRisk.symbol,
|
|
429
|
-
positionNotionalBaseCCY: Math.abs(newPosition),
|
|
430
|
-
side: newSide,
|
|
431
|
-
entryPrice: newEntryPrice,
|
|
432
|
-
leverage: newLeverage,
|
|
433
|
-
markPrice: markPrice,
|
|
434
|
-
unrealizedPnlQuoteCCY: newPosition * markPrice - newLockedInValue,
|
|
435
|
-
unrealizedFundingCollateralCCY: currentPositionRisk.unrealizedFundingCollateralCCY,
|
|
436
|
-
collateralCC: newCollateral,
|
|
437
|
-
collToQuoteConversion: indexPriceS3,
|
|
438
|
-
liquidationPrice: [S2Liq, S3Liq],
|
|
439
|
-
liquidationLvg: 1 / tau,
|
|
440
|
-
};
|
|
441
|
-
return newPositionRisk;
|
|
498
|
+
return [S2Liq, S3Liq, tau];
|
|
442
499
|
}
|
|
443
500
|
|
|
444
501
|
/**
|
|
@@ -727,17 +784,19 @@ export default class MarketData extends PerpetualDataHandler {
|
|
|
727
784
|
if (this.proxyContract == null) {
|
|
728
785
|
throw Error("no proxy contract initialized. Use createProxyInstance().");
|
|
729
786
|
}
|
|
730
|
-
|
|
731
|
-
traderAddr,
|
|
732
|
-
symbol,
|
|
733
|
-
this.symbolToPerpStaticInfo,
|
|
734
|
-
this.proxyContract
|
|
735
|
-
);
|
|
787
|
+
|
|
736
788
|
if (indexPrices == undefined) {
|
|
737
789
|
// fetch from API
|
|
738
790
|
let obj = await this.priceFeedGetter.fetchPricesForPerpetual(symbol);
|
|
739
791
|
indexPrices = [obj.idxPrices[0], obj.idxPrices[1]];
|
|
740
792
|
}
|
|
793
|
+
let mgnAcct = await PerpetualDataHandler.getMarginAccount(
|
|
794
|
+
traderAddr,
|
|
795
|
+
symbol,
|
|
796
|
+
this.symbolToPerpStaticInfo,
|
|
797
|
+
this.proxyContract,
|
|
798
|
+
[indexPrices[0], indexPrices[1]]
|
|
799
|
+
);
|
|
741
800
|
let S2 = indexPrices[0];
|
|
742
801
|
let ccyType = this.getPerpetualStaticInfo(symbol).collateralCurrencyType;
|
|
743
802
|
let S3 = ccyType == COLLATERAL_CURRENCY_QUANTO ? indexPrices[1] : ccyType == COLLATERAL_CURRENCY_QUOTE ? 1 : S2;
|
|
@@ -352,23 +352,19 @@ export default class PerpetualDataHandler {
|
|
|
352
352
|
symbol: string,
|
|
353
353
|
symbolToPerpStaticInfo: Map<string, PerpetualStaticInfo>,
|
|
354
354
|
_proxyContract: ethers.Contract,
|
|
355
|
-
_pxS2S3
|
|
355
|
+
_pxS2S3: [number, number]
|
|
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
|
-
// }
|
|
365
361
|
const idx_cash = 3;
|
|
366
362
|
const idx_notional = 4;
|
|
367
363
|
const idx_locked_in = 5;
|
|
368
364
|
const idx_mark_price = 8;
|
|
369
365
|
const idx_lvg = 7;
|
|
370
366
|
const idx_s3 = 9;
|
|
371
|
-
let traderState = await _proxyContract.getTraderState(perpId, traderAddr);
|
|
367
|
+
let traderState = await _proxyContract.getTraderState(perpId, traderAddr, _pxS2S3.map(x=>floatToABK64x64(x)));
|
|
372
368
|
let isEmpty = traderState[idx_notional] == 0;
|
|
373
369
|
let cash = ABK64x64ToFloat(traderState[idx_cash]);
|
|
374
370
|
let S2Liq = 0,
|
|
@@ -383,6 +379,7 @@ export default class PerpetualDataHandler {
|
|
|
383
379
|
[S2Liq, S3Liq, tau, pnl, unpaidFundingCC] = PerpetualDataHandler._calculateLiquidationPrice(
|
|
384
380
|
symbol,
|
|
385
381
|
traderState,
|
|
382
|
+
_pxS2S3[0],
|
|
386
383
|
symbolToPerpStaticInfo
|
|
387
384
|
);
|
|
388
385
|
fLockedIn = traderState[idx_locked_in];
|
|
@@ -469,12 +466,14 @@ export default class PerpetualDataHandler {
|
|
|
469
466
|
* Liquidation price
|
|
470
467
|
* @param symbol symbol of the form BTC-USD-MATIC
|
|
471
468
|
* @param traderState BigInt array according to smart contract
|
|
469
|
+
* @param S2 number, index price S2
|
|
472
470
|
* @param symbolToPerpStaticInfo mapping symbol->PerpStaticInfo
|
|
473
471
|
* @returns liquidation mark-price, corresponding collateral/quote conversion
|
|
474
472
|
*/
|
|
475
473
|
protected static _calculateLiquidationPrice(
|
|
476
474
|
symbol: string,
|
|
477
475
|
traderState: BigNumber[],
|
|
476
|
+
S2: number,
|
|
478
477
|
symbolToPerpStaticInfo: Map<string, PerpetualStaticInfo>
|
|
479
478
|
): [number, number, number, number, number] {
|
|
480
479
|
const idx_availableCashCC = 2;
|
|
@@ -501,7 +500,7 @@ export default class PerpetualDataHandler {
|
|
|
501
500
|
if (perpInfo.collateralCurrencyType == CollaterlCCY.BASE) {
|
|
502
501
|
S2Liq = calculateLiquidationPriceCollateralBase(lockedInValueQC, position, cashCC, tau);
|
|
503
502
|
S3Liq = S2Liq;
|
|
504
|
-
unpaidFunding = unpaidFunding /
|
|
503
|
+
unpaidFunding = unpaidFunding / S2;
|
|
505
504
|
} else if (perpInfo.collateralCurrencyType == CollaterlCCY.QUANTO) {
|
|
506
505
|
let S3 = S3Liq;
|
|
507
506
|
S3Liq = S3;
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const D8X_SDK_VERSION = "0.1.
|
|
1
|
+
export const D8X_SDK_VERSION = "0.1.4";
|