@d8x/perpetuals-sdk 0.0.39 → 0.0.41

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.
@@ -139,7 +139,15 @@ export default class MarketData extends PerpetualDataHandler {
139
139
  * @returns {MarginAccount} Position risk after trade
140
140
  */
141
141
  positionRiskOnTrade(traderAddr: string, order: Order, currentPositionRisk?: MarginAccount): Promise<MarginAccount>;
142
- protected static _positionRiskOnTrade(symbol: string, tradeAmount: number, tradeLeverage: number | undefined, keepPositionLvg: boolean | undefined, tradePrice: number, feeRate: number, perpetualState: PerpetualState, currentPositionRisk: MarginAccount, symbolToPerpStaticInfo: Map<string, PerpetualStaticInfo>): MarginAccount;
142
+ /**
143
+ * Estimates what the position risk will be if given amount of collateral is added/removed from the account.
144
+ * @param traderAddr Address of trader
145
+ * @param deltaCollateral Amount of collateral to add or remove (signed)
146
+ * @param currentPositionRisk Position risk before
147
+ * @returns {MarginAccount} Position risk after
148
+ */
149
+ positionRiskOnCollateralAction(deltaCollateral: number, currentPositionRisk: MarginAccount): Promise<MarginAccount>;
150
+ 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;
143
151
  maxOrderSizeForTrader(side: string, positionRisk: MarginAccount, perpetualState: PerpetualState): number;
144
152
  /**
145
153
  * Uses the Oracle(s) in the exchange to get the latest price of a given index in a given currency, if a route exists.
@@ -253,5 +261,6 @@ export default class MarketData extends PerpetualDataHandler {
253
261
  * @ignore
254
262
  */
255
263
  static orderIdsOfTrader(traderAddr: string, orderBookContract: ethers.Contract): Promise<string[]>;
264
+ getAvailableMargin(traderAddr: string, symbol: string): Promise<number>;
256
265
  static _exchangeInfo(_proxyContract: ethers.Contract, _poolStaticInfos: Array<PoolStaticInfo>, _symbolList: Map<string, string>): Promise<ExchangeInfo>;
257
266
  }
@@ -217,15 +217,33 @@ class MarketData extends perpetualDataHandler_1.default {
217
217
  let feeRate = (yield this.proxyContract.queryExchangeFee(poolId, traderAddr, (_b = order.brokerAddr) !== null && _b !== void 0 ? _b : nodeSDKTypes_1.ZERO_ADDRESS)) +
218
218
  ((_c = order.brokerFeeTbps) !== null && _c !== void 0 ? _c : 0) / 100000;
219
219
  let perpetualState = yield this.getPerpetualState(order.symbol);
220
- console.log("perpetualState", perpetualState);
221
- return MarketData._positionRiskOnTrade(order.symbol, tradeAmount, order.leverage, order.keepPositionLvg, tradePrice, feeRate, perpetualState, currentPositionRisk, this.symbolToPerpStaticInfo);
220
+ return MarketData._positionRiskOnAccountAction(order.symbol, tradeAmount, 0, order.leverage, order.keepPositionLvg, tradePrice, feeRate, perpetualState, currentPositionRisk, this.symbolToPerpStaticInfo);
222
221
  });
223
222
  }
224
- static _positionRiskOnTrade(symbol, tradeAmount, tradeLeverage, keepPositionLvg, tradePrice, feeRate, perpetualState, currentPositionRisk, symbolToPerpStaticInfo) {
223
+ /**
224
+ * Estimates what the position risk will be if given amount of collateral is added/removed from the account.
225
+ * @param traderAddr Address of trader
226
+ * @param deltaCollateral Amount of collateral to add or remove (signed)
227
+ * @param currentPositionRisk Position risk before
228
+ * @returns {MarginAccount} Position risk after
229
+ */
230
+ positionRiskOnCollateralAction(deltaCollateral, currentPositionRisk) {
231
+ return __awaiter(this, void 0, void 0, function* () {
232
+ if (this.proxyContract == null) {
233
+ throw Error("no proxy contract initialized. Use createProxyInstance().");
234
+ }
235
+ let perpetualState = yield this.getPerpetualState(currentPositionRisk.symbol);
236
+ return MarketData._positionRiskOnAccountAction(currentPositionRisk.symbol, 0, deltaCollateral, undefined, false, 0, 0, perpetualState, currentPositionRisk, this.symbolToPerpStaticInfo);
237
+ });
238
+ }
239
+ static _positionRiskOnAccountAction(symbol, tradeAmount, marginDeposit, tradeLeverage, keepPositionLvg, tradePrice, feeRate, perpetualState, currentPositionRisk, symbolToPerpStaticInfo) {
225
240
  let currentPosition = currentPositionRisk.positionNotionalBaseCCY;
226
241
  let newPosition = currentPositionRisk.positionNotionalBaseCCY + tradeAmount;
227
242
  let side = newPosition > 0 ? nodeSDKTypes_1.BUY_SIDE : newPosition < 0 ? nodeSDKTypes_1.SELL_SIDE : nodeSDKTypes_1.CLOSED_SIDE;
228
243
  let lockedInValue = currentPositionRisk.entryPrice * currentPosition;
244
+ if (tradeAmount == 0) {
245
+ keepPositionLvg = false;
246
+ }
229
247
  // need these for leverage/margin calculations
230
248
  let [markPrice, indexPriceS2, indexPriceS3] = [
231
249
  perpetualState.markPrice,
@@ -238,27 +256,21 @@ class MarketData extends perpetualDataHandler_1.default {
238
256
  // we have a target leverage for the resulting position
239
257
  // this gives us the total margin needed in the account so that it satisfies the leverage condition
240
258
  newCollateral = (0, d8XMath_1.getMarginRequiredForLeveragedTrade)(currentPositionRisk.leverage, currentPosition, lockedInValue, tradeAmount, markPrice, indexPriceS2, indexPriceS3, tradePrice, feeRate);
241
- /**
242
- * export function getDepositAmountForLvgTrade(
243
- pos0: number,
244
- b0: number,
245
- tradeAmnt: number,
246
- targetLvg: number,
247
- price: number,
248
- S3: number,
249
- S2Mark: number,
250
- maxLvg?: number
251
- */
252
259
  // the new leverage follows from the updated margin and position
253
260
  newLeverage = (0, d8XMath_1.getNewPositionLeverage)(tradeAmount, newCollateral, currentPosition, lockedInValue, indexPriceS2, indexPriceS3, markPrice, tradePrice, feeRate);
254
261
  }
255
- else {
262
+ else if (tradeAmount != 0) {
256
263
  // the order has its own leverage and margin requirements
257
264
  let tradeCollateral = (0, d8XMath_1.getMarginRequiredForLeveragedTrade)(tradeLeverage, 0, 0, tradeAmount, markPrice, indexPriceS2, indexPriceS3, tradePrice, feeRate);
258
265
  newCollateral = currentPositionRisk.collateralCC + tradeCollateral;
259
266
  // the new leverage corresponds to increasing the position and collateral according to the order
260
267
  newLeverage = (0, d8XMath_1.getNewPositionLeverage)(tradeAmount, newCollateral, currentPosition, lockedInValue, indexPriceS2, indexPriceS3, markPrice, tradePrice, feeRate);
261
268
  }
269
+ else {
270
+ // there is no order, adding/removing collateral
271
+ newCollateral = currentPositionRisk.collateralCC + marginDeposit;
272
+ newLeverage = (0, d8XMath_1.getNewPositionLeverage)(0, newCollateral, currentPosition, lockedInValue, indexPriceS2, indexPriceS3, markPrice, 0, 0);
273
+ }
262
274
  let newLockedInValue = lockedInValue + tradeAmount * tradePrice;
263
275
  // liquidation vars
264
276
  let S2Liq, S3Liq;
@@ -282,7 +294,7 @@ class MarketData extends perpetualDataHandler_1.default {
282
294
  entryPrice: Math.abs(newLockedInValue / newPosition),
283
295
  leverage: newLeverage,
284
296
  markPrice: markPrice,
285
- unrealizedPnlQuoteCCY: tradeAmount * (markPrice - tradePrice),
297
+ unrealizedPnlQuoteCCY: currentPositionRisk.unrealizedPnlQuoteCCY + tradeAmount * (markPrice - tradePrice),
286
298
  unrealizedFundingCollateralCCY: currentPositionRisk.unrealizedFundingCollateralCCY,
287
299
  collateralCC: newCollateral,
288
300
  collToQuoteConversion: indexPriceS3,
@@ -496,6 +508,19 @@ class MarketData extends perpetualDataHandler_1.default {
496
508
  return digests;
497
509
  });
498
510
  }
511
+ getAvailableMargin(traderAddr, symbol) {
512
+ return __awaiter(this, void 0, void 0, function* () {
513
+ if (this.proxyContract == null) {
514
+ throw Error("no proxy contract initialized. Use createProxyInstance().");
515
+ }
516
+ let mgnAcct = yield perpetualDataHandler_1.default.getMarginAccount(traderAddr, symbol, this.symbolToPerpStaticInfo, this.proxyContract);
517
+ let perpInfo = this.symbolToPerpStaticInfo.get(symbol);
518
+ let balanceCC = mgnAcct.collateralCC + mgnAcct.unrealizedPnlQuoteCCY / mgnAcct.collToQuoteConversion;
519
+ let initalMarginCC = Math.abs((perpInfo.initialMarginRate * mgnAcct.positionNotionalBaseCCY * mgnAcct.markPrice) /
520
+ mgnAcct.collToQuoteConversion);
521
+ return balanceCC - initalMarginCC;
522
+ });
523
+ }
499
524
  static _exchangeInfo(_proxyContract, _poolStaticInfos, _symbolList) {
500
525
  return __awaiter(this, void 0, void 0, function* () {
501
526
  let nestedPerpetualIDs = yield perpetualDataHandler_1.default.getNestedPerpetualIds(_proxyContract);
@@ -284,12 +284,12 @@ class OrderReferrerTool extends writeAccessHandler_1.default {
284
284
  if (order.quantity < perpetualDataHandler_1.default._getLotSize(order.symbol, symbolToPerpInfoMap)) {
285
285
  return false;
286
286
  }
287
- // check limit price, which may be undefined if it's an unrestricted market order
287
+ // check limit price: fromSmartContractOrder will set it to undefined when not tradeable
288
288
  if (order.limitPrice == undefined) {
289
- order.limitPrice = order.side == nodeSDKTypes_1.BUY_SIDE ? Infinity : 0;
289
+ return false;
290
290
  }
291
- if ((order.side == nodeSDKTypes_1.BUY_SIDE && orderPrice > order.limitPrice) ||
292
- (order.side == nodeSDKTypes_1.SELL_SIDE && orderPrice < order.limitPrice)) {
291
+ let limitPrice = order.limitPrice;
292
+ if ((order.side == nodeSDKTypes_1.BUY_SIDE && orderPrice > limitPrice) || (order.side == nodeSDKTypes_1.SELL_SIDE && orderPrice < limitPrice)) {
293
293
  return false;
294
294
  }
295
295
  // do we need to check trigger/stop?
@@ -337,7 +337,8 @@ class PerpetualDataHandler {
337
337
  else {
338
338
  S2Liq = (0, d8XMath_1.calculateLiquidationPriceCollateralQuote)(lockedInValueQC, position, cashCC, tau);
339
339
  }
340
- let pnl = position * Sm - lockedInValueQC - unpaidFunding;
340
+ // account cash + pnl = avail cash + pos Sm - L = margin balance
341
+ let pnl = position * Sm - lockedInValueQC + unpaidFunding;
341
342
  return [S2Liq, S3Liq, tau, pnl, unpaidFundingCC];
342
343
  }
343
344
  /**
@@ -405,8 +406,11 @@ class PerpetualDataHandler {
405
406
  let side = order.fAmount > 0 ? nodeSDKTypes_1.BUY_SIDE : nodeSDKTypes_1.SELL_SIDE;
406
407
  let limitPrice, stopPrice;
407
408
  let fLimitPrice = ethers_1.BigNumber.from(order.fLimitPrice);
408
- if (fLimitPrice.eq(0) || fLimitPrice.eq(nodeSDKTypes_1.MAX_64x64)) {
409
- limitPrice = undefined;
409
+ if (fLimitPrice.eq(0)) {
410
+ limitPrice = side == nodeSDKTypes_1.BUY_SIDE ? undefined : 0;
411
+ }
412
+ else if (fLimitPrice.eq(nodeSDKTypes_1.MAX_64x64)) {
413
+ limitPrice = side == nodeSDKTypes_1.BUY_SIDE ? Infinity : undefined;
410
414
  }
411
415
  else {
412
416
  limitPrice = (0, d8XMath_1.ABK64x64ToFloat)(fLimitPrice);
@@ -49,7 +49,7 @@ class WriteAccessHandler extends perpetualDataHandler_1.default {
49
49
  createProxyInstance(provider) {
50
50
  return __awaiter(this, void 0, void 0, function* () {
51
51
  if (provider == undefined) {
52
- this.provider = new ethers_1.ethers.providers.JsonRpcProvider(this.nodeURL);
52
+ this.provider = new ethers_1.ethers.providers.JsonRpcBatchProvider(this.nodeURL);
53
53
  }
54
54
  else {
55
55
  this.provider = provider;
package/package.json CHANGED
@@ -32,7 +32,7 @@
32
32
  },
33
33
  "name": "@d8x/perpetuals-sdk",
34
34
  "description": "Node TypeScript SDK for D8X Perpetual Futures",
35
- "version": "0.0.39",
35
+ "version": "0.0.41",
36
36
  "main": "./dist/index.js",
37
37
  "types": "./dist/index.d.ts",
38
38
  "directories": {
package/src/marketData.ts CHANGED
@@ -237,10 +237,11 @@ export default class MarketData extends PerpetualDataHandler {
237
237
  (await this.proxyContract.queryExchangeFee(poolId, traderAddr, order.brokerAddr ?? ZERO_ADDRESS)) +
238
238
  (order.brokerFeeTbps ?? 0) / 100_000;
239
239
  let perpetualState = await this.getPerpetualState(order.symbol);
240
- console.log("perpetualState", perpetualState);
241
- return MarketData._positionRiskOnTrade(
240
+
241
+ return MarketData._positionRiskOnAccountAction(
242
242
  order.symbol,
243
243
  tradeAmount,
244
+ 0,
244
245
  order.leverage,
245
246
  order.keepPositionLvg,
246
247
  tradePrice,
@@ -251,9 +252,40 @@ export default class MarketData extends PerpetualDataHandler {
251
252
  );
252
253
  }
253
254
 
254
- protected static _positionRiskOnTrade(
255
+ /**
256
+ * Estimates what the position risk will be if given amount of collateral is added/removed from the account.
257
+ * @param traderAddr Address of trader
258
+ * @param deltaCollateral Amount of collateral to add or remove (signed)
259
+ * @param currentPositionRisk Position risk before
260
+ * @returns {MarginAccount} Position risk after
261
+ */
262
+ public async positionRiskOnCollateralAction(
263
+ deltaCollateral: number,
264
+ currentPositionRisk: MarginAccount
265
+ ): Promise<MarginAccount> {
266
+ if (this.proxyContract == null) {
267
+ throw Error("no proxy contract initialized. Use createProxyInstance().");
268
+ }
269
+ let perpetualState = await this.getPerpetualState(currentPositionRisk.symbol);
270
+
271
+ return MarketData._positionRiskOnAccountAction(
272
+ currentPositionRisk.symbol,
273
+ 0,
274
+ deltaCollateral,
275
+ undefined,
276
+ false,
277
+ 0,
278
+ 0,
279
+ perpetualState,
280
+ currentPositionRisk,
281
+ this.symbolToPerpStaticInfo
282
+ );
283
+ }
284
+
285
+ protected static _positionRiskOnAccountAction(
255
286
  symbol: string,
256
287
  tradeAmount: number,
288
+ marginDeposit: number,
257
289
  tradeLeverage: number | undefined,
258
290
  keepPositionLvg: boolean | undefined,
259
291
  tradePrice: number,
@@ -266,7 +298,9 @@ export default class MarketData extends PerpetualDataHandler {
266
298
  let newPosition = currentPositionRisk.positionNotionalBaseCCY + tradeAmount;
267
299
  let side = newPosition > 0 ? BUY_SIDE : newPosition < 0 ? SELL_SIDE : CLOSED_SIDE;
268
300
  let lockedInValue = currentPositionRisk.entryPrice * currentPosition;
269
-
301
+ if (tradeAmount == 0) {
302
+ keepPositionLvg = false;
303
+ }
270
304
  // need these for leverage/margin calculations
271
305
  let [markPrice, indexPriceS2, indexPriceS3] = [
272
306
  perpetualState.markPrice,
@@ -289,17 +323,6 @@ export default class MarketData extends PerpetualDataHandler {
289
323
  tradePrice,
290
324
  feeRate
291
325
  );
292
- /**
293
- * export function getDepositAmountForLvgTrade(
294
- pos0: number,
295
- b0: number,
296
- tradeAmnt: number,
297
- targetLvg: number,
298
- price: number,
299
- S3: number,
300
- S2Mark: number,
301
- maxLvg?: number
302
- */
303
326
  // the new leverage follows from the updated margin and position
304
327
  newLeverage = getNewPositionLeverage(
305
328
  tradeAmount,
@@ -312,7 +335,7 @@ export default class MarketData extends PerpetualDataHandler {
312
335
  tradePrice,
313
336
  feeRate
314
337
  );
315
- } else {
338
+ } else if (tradeAmount != 0) {
316
339
  // the order has its own leverage and margin requirements
317
340
  let tradeCollateral = getMarginRequiredForLeveragedTrade(
318
341
  tradeLeverage,
@@ -338,6 +361,20 @@ export default class MarketData extends PerpetualDataHandler {
338
361
  tradePrice,
339
362
  feeRate
340
363
  );
364
+ } else {
365
+ // there is no order, adding/removing collateral
366
+ newCollateral = currentPositionRisk.collateralCC + marginDeposit;
367
+ newLeverage = getNewPositionLeverage(
368
+ 0,
369
+ newCollateral,
370
+ currentPosition,
371
+ lockedInValue,
372
+ indexPriceS2,
373
+ indexPriceS3,
374
+ markPrice,
375
+ 0,
376
+ 0
377
+ );
341
378
  }
342
379
  let newLockedInValue = lockedInValue + tradeAmount * tradePrice;
343
380
 
@@ -368,7 +405,7 @@ export default class MarketData extends PerpetualDataHandler {
368
405
  entryPrice: Math.abs(newLockedInValue / newPosition),
369
406
  leverage: newLeverage,
370
407
  markPrice: markPrice,
371
- unrealizedPnlQuoteCCY: tradeAmount * (markPrice - tradePrice),
408
+ unrealizedPnlQuoteCCY: currentPositionRisk.unrealizedPnlQuoteCCY + tradeAmount * (markPrice - tradePrice),
372
409
  unrealizedFundingCollateralCCY: currentPositionRisk.unrealizedFundingCollateralCCY,
373
410
  collateralCC: newCollateral,
374
411
  collToQuoteConversion: indexPriceS3,
@@ -597,6 +634,25 @@ export default class MarketData extends PerpetualDataHandler {
597
634
  return digests;
598
635
  }
599
636
 
637
+ public async getAvailableMargin(traderAddr: string, symbol: string): Promise<number> {
638
+ if (this.proxyContract == null) {
639
+ throw Error("no proxy contract initialized. Use createProxyInstance().");
640
+ }
641
+ let mgnAcct = await PerpetualDataHandler.getMarginAccount(
642
+ traderAddr,
643
+ symbol,
644
+ this.symbolToPerpStaticInfo,
645
+ this.proxyContract
646
+ );
647
+ let perpInfo = this.symbolToPerpStaticInfo.get(symbol);
648
+ let balanceCC = mgnAcct.collateralCC + mgnAcct.unrealizedPnlQuoteCCY / mgnAcct.collToQuoteConversion;
649
+ let initalMarginCC = Math.abs(
650
+ (perpInfo!.initialMarginRate * mgnAcct.positionNotionalBaseCCY * mgnAcct.markPrice) /
651
+ mgnAcct.collToQuoteConversion
652
+ );
653
+ return balanceCC - initalMarginCC;
654
+ }
655
+
600
656
  public static async _exchangeInfo(
601
657
  _proxyContract: ethers.Contract,
602
658
  _poolStaticInfos: Array<PoolStaticInfo>,
@@ -311,15 +311,12 @@ export default class OrderReferrerTool extends WriteAccessHandler {
311
311
  if (order.quantity < PerpetualDataHandler._getLotSize(order.symbol, symbolToPerpInfoMap)) {
312
312
  return false;
313
313
  }
314
- // check limit price, which may be undefined if it's an unrestricted market order
314
+ // check limit price: fromSmartContractOrder will set it to undefined when not tradeable
315
315
  if (order.limitPrice == undefined) {
316
- order.limitPrice = order.side == BUY_SIDE ? Infinity : 0;
316
+ return false;
317
317
  }
318
-
319
- if (
320
- (order.side == BUY_SIDE && orderPrice > order.limitPrice) ||
321
- (order.side == SELL_SIDE && orderPrice < order.limitPrice)
322
- ) {
318
+ let limitPrice = order.limitPrice!;
319
+ if ((order.side == BUY_SIDE && orderPrice > limitPrice) || (order.side == SELL_SIDE && orderPrice < limitPrice)) {
323
320
  return false;
324
321
  }
325
322
  // do we need to check trigger/stop?
@@ -426,7 +426,8 @@ export default class PerpetualDataHandler {
426
426
  } else {
427
427
  S2Liq = calculateLiquidationPriceCollateralQuote(lockedInValueQC, position, cashCC, tau);
428
428
  }
429
- let pnl = position * Sm - lockedInValueQC - unpaidFunding;
429
+ // account cash + pnl = avail cash + pos Sm - L = margin balance
430
+ let pnl = position * Sm - lockedInValueQC + unpaidFunding;
430
431
  return [S2Liq, S3Liq, tau, pnl, unpaidFundingCC];
431
432
  }
432
433
 
@@ -506,8 +507,10 @@ export default class PerpetualDataHandler {
506
507
  let side = order.fAmount > 0 ? BUY_SIDE : SELL_SIDE;
507
508
  let limitPrice, stopPrice;
508
509
  let fLimitPrice: BigNumber | undefined = BigNumber.from(order.fLimitPrice);
509
- if (fLimitPrice.eq(0) || fLimitPrice.eq(MAX_64x64)) {
510
- limitPrice = undefined;
510
+ if (fLimitPrice.eq(0)) {
511
+ limitPrice = side == BUY_SIDE ? undefined : 0;
512
+ } else if (fLimitPrice.eq(MAX_64x64)) {
513
+ limitPrice = side == BUY_SIDE ? Infinity : undefined;
511
514
  } else {
512
515
  limitPrice = ABK64x64ToFloat(fLimitPrice);
513
516
  }
@@ -37,7 +37,7 @@ export default class WriteAccessHandler extends PerpetualDataHandler {
37
37
  */
38
38
  public async createProxyInstance(provider?: ethers.providers.JsonRpcProvider) {
39
39
  if (provider == undefined) {
40
- this.provider = new ethers.providers.JsonRpcProvider(this.nodeURL);
40
+ this.provider = new ethers.providers.JsonRpcBatchProvider(this.nodeURL);
41
41
  } else {
42
42
  this.provider = provider;
43
43
  }