@d8x/perpetuals-sdk 2.6.22 → 2.7.0

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.
Files changed (58) hide show
  1. package/dist/cjs/config/defaultConfig.json +2 -2
  2. package/dist/cjs/constants.js.map +1 -1
  3. package/dist/cjs/d8XMath.d.ts +42 -25
  4. package/dist/cjs/d8XMath.js +188 -151
  5. package/dist/cjs/d8XMath.js.map +1 -1
  6. package/dist/cjs/liquidatorTool.d.ts +1 -1
  7. package/dist/cjs/liquidatorTool.js +9 -9
  8. package/dist/cjs/liquidatorTool.js.map +1 -1
  9. package/dist/cjs/marketData.d.ts +3 -3
  10. package/dist/cjs/marketData.js +9 -8
  11. package/dist/cjs/marketData.js.map +1 -1
  12. package/dist/cjs/perpetualDataHandler.d.ts +3 -3
  13. package/dist/cjs/perpetualDataHandler.js +7 -5
  14. package/dist/cjs/perpetualDataHandler.js.map +1 -1
  15. package/dist/cjs/polyMktsPxFeed.js +2 -2
  16. package/dist/cjs/polyMktsPxFeed.js.map +1 -1
  17. package/dist/cjs/priceFeeds.d.ts +1 -0
  18. package/dist/cjs/priceFeeds.js +18 -2
  19. package/dist/cjs/priceFeeds.js.map +1 -1
  20. package/dist/cjs/utils.d.ts +3 -3
  21. package/dist/cjs/utils.js.map +1 -1
  22. package/dist/cjs/version.d.ts +1 -1
  23. package/dist/cjs/version.js +1 -1
  24. package/dist/esm/config/defaultConfig.json +2 -2
  25. package/dist/esm/constants.js.map +1 -1
  26. package/dist/esm/d8XMath.d.ts +42 -25
  27. package/dist/esm/d8XMath.js +187 -150
  28. package/dist/esm/d8XMath.js.map +1 -1
  29. package/dist/esm/liquidatorTool.d.ts +1 -1
  30. package/dist/esm/liquidatorTool.js +10 -10
  31. package/dist/esm/liquidatorTool.js.map +1 -1
  32. package/dist/esm/marketData.d.ts +3 -3
  33. package/dist/esm/marketData.js +9 -8
  34. package/dist/esm/marketData.js.map +1 -1
  35. package/dist/esm/perpetualDataHandler.d.ts +3 -3
  36. package/dist/esm/perpetualDataHandler.js +7 -5
  37. package/dist/esm/perpetualDataHandler.js.map +1 -1
  38. package/dist/esm/polyMktsPxFeed.js +2 -2
  39. package/dist/esm/polyMktsPxFeed.js.map +1 -1
  40. package/dist/esm/priceFeeds.d.ts +1 -0
  41. package/dist/esm/priceFeeds.js +18 -2
  42. package/dist/esm/priceFeeds.js.map +1 -1
  43. package/dist/esm/utils.d.ts +3 -3
  44. package/dist/esm/utils.js.map +1 -1
  45. package/dist/esm/version.d.ts +1 -1
  46. package/dist/esm/version.js +1 -1
  47. package/doc/d8x-perpetuals-sdk.md +116 -64
  48. package/package.json +1 -1
  49. package/src/config/defaultConfig.json +2 -2
  50. package/src/constants.ts +0 -1
  51. package/src/d8XMath.ts +210 -167
  52. package/src/liquidatorTool.ts +16 -10
  53. package/src/marketData.ts +38 -25
  54. package/src/perpetualDataHandler.ts +13 -9
  55. package/src/polyMktsPxFeed.ts +6 -7
  56. package/src/priceFeeds.ts +19 -3
  57. package/src/utils.ts +3 -3
  58. package/src/version.ts +1 -1
package/src/d8XMath.ts CHANGED
@@ -410,7 +410,7 @@ export function getNewPositionLeverage(
410
410
  * @param {number} price - price to trade amount 'tradeAmnt'
411
411
  * @param {number} S3 - collateral to quote conversion (=S2 if base-collateral, =1 if quote collateral, = index S3 if quanto)
412
412
  * @param {number} S2Mark - mark price
413
- * @param {boolean} isPredMkt - true if prediction market
413
+ * @param {number} cmin - Absolute minimum margin per contract, only for pred markets
414
414
  * @returns {number} Amount to be deposited to have the given leverage when trading into position pos before fees
415
415
  */
416
416
  export function getDepositAmountForLvgTrade(
@@ -421,29 +421,112 @@ export function getDepositAmountForLvgTrade(
421
421
  price: number,
422
422
  S3: number,
423
423
  S2Mark: number,
424
- isPredMkt: boolean
424
+ cmin: number | undefined
425
425
  ) {
426
- let pnl = (tradeAmnt * (S2Mark - price)) / S3;
427
- let S2MarkBefore = S2Mark;
428
- if (isPredMkt) {
429
- // adjust mark price to 'probability'
430
- S2Mark = S2Mark - 1;
431
- S2MarkBefore = S2Mark;
432
- if (pos0 < 0) {
433
- S2MarkBefore = 1 - S2Mark;
434
- }
435
- if (pos0 + tradeAmnt < 0) {
436
- S2Mark = 1 - S2Mark;
426
+ if (cmin && cmin > 0) {
427
+ // TODO: c0?
428
+ if (b0 != 0) {
429
+ console.log("b0 != 0");
437
430
  }
431
+ return getDepositAmountForPredMktLvgTrade(pos0, b0, 0, tradeAmnt, targetLvg, price - 1, S3, S2Mark - 1, cmin);
438
432
  }
433
+ let pnl = (tradeAmnt * (S2Mark - price)) / S3;
439
434
  if (targetLvg == 0) {
440
435
  // use current leverage
441
- targetLvg = (Math.abs(pos0) * S2MarkBefore) / S3 / b0;
436
+ targetLvg = (Math.abs(pos0) * S2Mark) / S3 / b0;
442
437
  }
443
438
  let b = (Math.abs(pos0 + tradeAmnt) * S2Mark) / S3 / targetLvg;
444
439
  return -(b0 + pnl - b);
445
440
  }
446
441
 
442
+ /**
443
+ * Determine amount to be deposited into margin account so that the given leverage
444
+ * is obtained when opening a prediction market position
445
+ * Does NOT include fees, but accounts for a possible non-zero current position
446
+ * Smart contract equivalent: getDepositAmountForPredMktLvgPosition
447
+ * @param {number} pos0 - current position
448
+ * @param {number} b0 - current balance
449
+ * @param {number} c0 - current available cash
450
+ * @param {number} tradeAmnt - amount to trade
451
+ * @param {number} targetLvg - target leverage
452
+ * @param {number} prob - prob to trade amount 'tradeAmnt'
453
+ * @param {number} S3 - collateral to quote conversion (=S2 if base-collateral, =1 if quote collateral, = index S3 if quanto)
454
+ * @param {number} markProb - mark prob
455
+ * @param {number} imr - minimum absolute margin per contract (fInitialMarginRate)
456
+ * @returns {number} Amount to be deposited to have the given leverage when trading into position pos before fees
457
+ */
458
+ export function getDepositAmountForPredMktLvgTrade(
459
+ pos0: number,
460
+ b0: number,
461
+ c0: number,
462
+ tradeAmnt: number,
463
+ targetLvg: number,
464
+ prob: number,
465
+ S3: number,
466
+ markProb: number,
467
+ imr: number
468
+ ) {
469
+ /**
470
+ * Smart contract implementation:
471
+ // find smallest x such that:
472
+ // bal * s3 >= pos value / lvg
473
+ // where:
474
+ // pos value / lvg = |pos| * R(pm, sign(pos)) * margin rate
475
+ // pos = pos0 + k
476
+ // cash = cash0 + x
477
+ // ell = ell0 + px * k
478
+ // bal * s3 = cash * s3 + pos * sm - ell
479
+ // = bal0 * s3 + x * s3 + k * (sm - px)
480
+
481
+ // subject to:
482
+ // x >= 0
483
+ // cash * s3 >= |pos| * min(cmin, prob(sign(pos)))
484
+ // k * (sm - px) <= 0 a.s.
485
+ // (positive pnl does not contribute, i.e. ignore px better than mark)
486
+
487
+ // solution:
488
+ // bal0 * s3 + x * s3 >= pos value / lvg + (k * (px - sm))_+ = v * s3
489
+ // -->
490
+ // x >= v + (cash0 - bal0)_+ - cash0 = v - min(bal0, cash0)
491
+ // = pos value / lvg/ s3 + (k * (px - sm))_+ / s3 - min (bal0, cash0)
492
+ // = A + B - C
493
+ // x >= |pos| * min(cmin, prob(sign(pos))) / s3 - cash0
494
+ // x >= 0
495
+
496
+ // init x = A = pos value / lvg / s3
497
+ int128 fNewPos = _fPosition0.add(_fTradeAmount);
498
+ int128 v = (
499
+ fNewPos > 0 ? fNewPos.mul(_fMarkProb) : fNewPos.neg().mul(ONE_64x64.sub(_fMarkProb))
500
+ ).mul(_fMarginRate).div(_fS3);
501
+ // + B = max(0,k * (px - sm)) / s3
502
+ {
503
+ int128 fPnL = _fTradeAmount.mul(_fMarkProb.sub(_fTradeProb));
504
+ if (fPnL < 0) {
505
+ v = v.sub(fPnL.div(_fS3)); // pnl < 0 -> increase v
506
+ }
507
+ }
508
+ // - C = - min(bal0, cash0) = - Equity
509
+ {
510
+ int128 equity = _fCash0CC < _fBalance0 ? _fCash0CC : _fBalance0;
511
+ v = v.sub(equity); // equity can be used / must be covered if negative
512
+ }
513
+ return v > 0 ? v : int128(0);
514
+ */
515
+
516
+ const newPos = pos0 + tradeAmnt;
517
+ const posProb = newPos > 0 ? markProb : 1 - markProb; // R(pm, sign(new pos))
518
+ const maxLvg = pmMaxLeverage(newPos, markProb, imr);
519
+ targetLvg = targetLvg > maxLvg ? maxLvg : targetLvg;
520
+ const posValue = (Math.abs(newPos) * posProb) / S3;
521
+ const tradeLoss = Math.max(0, tradeAmnt * (prob - markProb)) / S3;
522
+ const curEquity = Math.min(c0, b0);
523
+ return Math.max(posValue / targetLvg + tradeLoss - curEquity, 0);
524
+ }
525
+
526
+ function pmMaxLeverage(posSign: number, markProb: number, minMarginPerCtrct: number) {
527
+ return Math.round(100 * (posSign > 0 ? markProb / minMarginPerCtrct : (1 - markProb) / minMarginPerCtrct)) / 100;
528
+ }
529
+
447
530
  /**
448
531
  * Convert a perpetual price to probability (predtictive markets)
449
532
  * @param px Perpetual price
@@ -459,7 +542,7 @@ export function priceToProb(px: number) {
459
542
  * @returns Perpetual price
460
543
  */
461
544
  export function probToPrice(prob: number) {
462
- return 1 + prob;
545
+ return Math.max(1, Math.min(2, 1 + prob));
463
546
  }
464
547
 
465
548
  // shannon entropy
@@ -473,118 +556,58 @@ export function entropy(prob: number) {
473
556
  /**
474
557
  * Maintenance margin requirement for prediction markets
475
558
  * @param pos signed position
559
+ * @param lockedInQC locked in value
476
560
  * @param s2 mark price
477
561
  * @param s3 collateral to quote conversion
478
562
  * @param m base margin rate
479
563
  * @returns required margin balance
480
564
  */
481
- function pmMarginThresh(pos: number, s2: number, s3: number, m: number | undefined = 0.18) {
482
- let p = s2 - 1;
483
- if (pos < 0) {
484
- p = 1 - p;
485
- }
486
- const h = entropy(p);
487
- const tau = m + (0.4 - m) * h;
488
- return (Math.abs(pos) * p * tau) / s3;
565
+ function pmMarginThresh(pos: number, lockedInQC: number, s2: number, s3: number, m: number):number {
566
+ return (pmMaintenanceMarginRate(pos, lockedInQC, s2, m) * Math.abs(pos)) / s3;
489
567
  }
490
568
 
491
569
  /**
492
570
  * Maintenance margin rate for prediction markets.
493
- * @param posSign sign of position in base currency (can be signed position or -1, 1)
571
+ * @param position signed position in base currency
572
+ * @param lockedInQC locked in value, p or 1-p times number of contracts
494
573
  * @param sm mark-price (=1+p)
495
- * @param m max margin rate from fInitialMarginRate
496
- * @returns margin rate to be applied (Math.abs(pos) * p * tau) / s3;
497
- */
498
- export function pmMaintenanceMarginRate(posSign: number, sm: number, m: number | undefined = 0.18): number {
499
- let p = sm - 1;
500
- if (posSign < 0) {
501
- p = 1 - p;
574
+ * @param m absolute maintenance buffer per contract (mu_m, fMaintenanceMarginRate)
575
+ * @returns {number} The margin rate to be applied: (Math.abs(pos) * p * tau) / s3
576
+ */
577
+ export function pmMaintenanceMarginRate(position: number, lockedInQC: number, sm: number, m: number): number {
578
+ let pm = sm - 1;
579
+ let entryP = position == 0 ? pm : Math.abs(lockedInQC / position) - 1;
580
+ if (position < 0) {
581
+ pm = 1 - pm;
582
+ entryP = 1 - entryP;
502
583
  }
503
- const h = entropy(p);
504
- return m + (0.4 - m) * h;
584
+ const L = Math.max(entryP - pm, 0);
585
+ const balAtLiq = Math.min(m + L, entryP) * Math.abs(position) + position * sm - lockedInQC;
586
+ return balAtLiq / (Math.abs(position) * pm);
505
587
  }
506
588
 
507
589
  /**
508
- * Maintenance margin rate for prediction markets.
590
+ * Initial margin rate for prediction markets.
509
591
  * @param posSign sign of position in base currency (can be signed position or -1, 1)
592
+ * @param s0 trade price
510
593
  * @param sm mark-price (=1+p)
511
- * @param m max margin rate from fMaintenanceMarginRate
512
- * @returns margin rate to be applied (Math.abs(pos) * p * tau) / s3;
594
+ * @param cmin Absolute min margin saved as `fInitialMarginRate`
595
+ * @returns {number} The margin rate to be applied: `(Math.abs(pos) * p * tau) / s3`
513
596
  */
514
- export function pmInitialMarginRate(posSign: number, sm: number, m: number | undefined = 0.2): number {
515
- let p = sm - 1;
597
+ export function pmInitialMarginRate(posSign: number, s0: number, sm: number, cmin: number): number {
598
+ let pm = sm - 1;
599
+ let p0 = s0 - 1;
516
600
  if (posSign < 0) {
517
- p = 1 - p;
601
+ pm = 1 - pm; // R(p_mark, sign(pos))
602
+ p0 = 1 - p0; // R(p_entry, sign(pos))
518
603
  }
519
- const h = entropy(p);
520
- return m + (0.5 - m) * h;
521
- }
522
-
523
- /**
524
- * Calculate the expected loss for a prediction market trade used for
525
- * prediction market fees
526
- * @param p probability derived from mark price (long)
527
- * @param m maximal maintenance rate from which we defer the actual maintenance margin rate
528
- * @param totLong total long in base currency
529
- * @param totShort total short
530
- * @param tradeAmt signed trade amount, can be zero
531
- * @param tradeMgnRate margin rate of the trader
532
- * @returns expected loss in dollars
533
- */
534
- export function expectedLoss(
535
- p: number,
536
- m: number,
537
- totLong: number,
538
- totShort: number,
539
- tradeAmt: number,
540
- tradeMgnRate: number
541
- ): number {
542
- // maintenance margin rate
543
- m = (0.4 - m) * entropy(p) + m;
544
- let dlm = 0;
545
- let dl = 0;
546
- let dsm = 0;
547
- let ds = 0;
548
- if (tradeAmt > 0) {
549
- dlm = p * tradeAmt * tradeMgnRate;
550
- dl = tradeAmt;
551
- } else if (tradeAmt < 0) {
552
- dsm = (1 - p) * Math.abs(tradeAmt) * tradeMgnRate;
553
- ds = Math.abs(tradeAmt);
554
- }
555
- const a = dl + totLong - m * totShort - dsm;
556
- const b = ds + totShort - m * totLong - dlm;
557
- return p * (1 - p) * Math.max(0, a + b);
558
- }
559
-
560
- /**
561
- * Equivalent to
562
- * const el0 = expectedLoss(prob, m, totLong, totShort, 0, 0);
563
- * const el1 = expectedLoss(prob, m, totLong, totShort, tradeAmt, tradeMgnRate)
564
- * const fee = (el1 - el0) / Math.abs(tradeAmt);
565
- * @param p prob long probability
566
- * @param m max maintenance margin rate (0.18)
567
- * @param tradeAmt trade amount in base currency
568
- * @param tradeMgnRate margin rate for this trade
569
- * @returns dollar fee
570
- */
571
- function expectedLossImpact(p: number, m: number, tradeAmt: number, tradeMgnRate: number) {
572
- m = (0.4 - m) * entropy(p) + m;
573
- let dlm = 0;
574
- let dl = 0;
575
- let dsm = 0;
576
- let ds = 0;
577
- if (tradeAmt > 0) {
578
- dlm = p * tradeAmt * tradeMgnRate;
579
- dl = tradeAmt;
580
- } else if (tradeAmt < 0) {
581
- dsm = (1 - p) * Math.abs(tradeAmt) * tradeMgnRate;
582
- ds = Math.abs(tradeAmt);
583
- }
584
- //long: p * (1 - p) max(0, dl-dlm) = p * (1 - p) max(0, tradeAmt - p * tradeAmt * tradeMgnRate)
585
- const a = dl - dsm;
586
- const b = ds - dlm;
587
- return p * (1 - p) * Math.max(0, a + b);
604
+ // mu0 = max(Rm/lvg, min(Rm, cmin))
605
+ // balance = (mu0 * |k| + k *(sm - s0)) / s3
606
+ // pos value = |k| * Rm / s3
607
+ // at max init lvg: Rm/lvg = min(cmin, Rm)
608
+ // --> margin rate = (mu0 + Rm - R0) / Rm
609
+ const mu0 = Math.min(pm, cmin) + Math.max(0, p0 - pm);
610
+ return (mu0 + pm - p0) / pm;
588
611
  }
589
612
 
590
613
  /**
@@ -597,13 +620,12 @@ function expectedLossImpact(p: number, m: number, tradeAmt: number, tradeMgnRate
597
620
  * @returns dollar fee relative to tradeAmt
598
621
  */
599
622
  export function pmExchangeFee(prob: number, m: number, tradeAmt: number, tradeMgnRate: number): number {
600
- /*
601
- equivalent:
602
- const el0 = expectedLoss(prob, m, totLong, totShort, 0, 0);
603
- const el1 = expectedLoss(prob, m, totLong, totShort, tradeAmt, tradeMgnRate);
604
- const fee = (el1 - el0) / Math.abs(tradeAmt);
605
- */
606
- let fee = expectedLossImpact(prob, m, tradeAmt, tradeMgnRate) / Math.abs(tradeAmt);
623
+ // TODO: port contract logic here
624
+ const [kappa, es] = [0, 0];
625
+ prob = tradeAmt > 0 ? prob : 1 - prob;
626
+ let fee = prob * (1 - kappa);
627
+ const scaledLvg = prob * tradeMgnRate * (1 - fee);
628
+ fee = fee * (1 - prob) - scaledLvg + es;
607
629
  return Math.max(fee, 0.001);
608
630
  }
609
631
 
@@ -620,45 +642,53 @@ export function pmMarginBalance(pos: number, s2: number, s3: number, ell: number
620
642
  return (pos * s2) / s3 - ell / s3 + mc;
621
643
  }
622
644
 
623
- export function pmExcessBalance(
624
- pos: number,
625
- s2: number,
626
- s3: number,
627
- ell: number,
628
- mc: number,
629
- m: number | undefined
630
- ): number {
631
- return pmMarginBalance(pos, s2, s3, ell, mc) - pmMarginThresh(pos, s2, s3, m);
645
+ export function pmExcessBalance(pos: number, s2: number, s3: number, ell: number, mc: number, m: number): number {
646
+ return pmMarginBalance(pos, s2, s3, ell, mc) - pmMarginThresh(pos, ell, s2, s3, m);
632
647
  }
633
648
 
634
- // finds the liquidation price for prediction markets
635
- // using Newton's algorithm
649
+ /**
650
+ *
651
+ * @param pos Signed position size
652
+ * @param s3 Collateral to quote conversion at spot
653
+ * @param ell Locked-in value
654
+ * @param mc Margin collateral
655
+ * @param baseMarginRate Maintenance margin per contract (mu_m)
656
+ * @param sm Mark price at entry
657
+ * @returns {number} Liquidation price as a probability in the range [0, 1]
658
+ */
636
659
  export function pmFindLiquidationPrice(
637
660
  pos: number,
638
661
  s3: number,
639
662
  ell: number,
640
663
  mc: number,
641
- baseMarginRate: number | undefined,
642
- s2Start: number | undefined = 0.5
664
+ baseMarginRate: number
643
665
  ): number {
644
- const delta_s = 0.01;
645
- let s = 100;
646
- let s_new = s2Start;
647
-
648
- while (Math.abs(s_new - s) > 0.01) {
649
- s = s_new;
650
- const f = Math.pow(pmExcessBalance(pos, s, s3, ell, mc, baseMarginRate), 2);
651
- const ds = (Math.pow(pmExcessBalance(pos, s + delta_s, s3, ell, mc, baseMarginRate), 2) - f) / delta_s;
652
- s_new = s - f / ds;
653
-
654
- if (s_new < 1) {
655
- return 1;
656
- }
657
- if (s_new > 2) {
658
- return 2;
659
- }
666
+ // liq <--> (A) c / |k| < R0 && (B) E < |k| * mu_m
667
+
668
+ // if not (A), return 0 (long) or 1 (short) [no liq]
669
+
670
+ // else, solve for pm:
671
+ // E = c - |k| max(0, s * (p0 - pm)) = |k| * mu_m
672
+ // if c/|k| < mu_m:
673
+ // any number would do --> return 1 (long) or 0 (short)
674
+ // else:
675
+ // pm = p0 - s * (c/|k| - mu_m)
676
+
677
+ const p0 = Math.abs(ell / pos) - 1;
678
+ const R0 = pos > 0 ? p0 : 1 - p0;
679
+ const excessPerCtrct = (mc * s3) / Math.abs(pos) - baseMarginRate; // c/|k| - mu_m, mu_m < CMINUS
680
+
681
+ if (mc * s3 > R0 * Math.abs(pos)) {
682
+ // c > |k| R(p0, s) --> no liquidation
683
+ return probToPrice(pos > 0 ? 0.0001 : 0.9999);
660
684
  }
661
- return s;
685
+
686
+ if (excessPerCtrct < 0) {
687
+ // already underwater
688
+ return probToPrice(pos > 0 ? 0.9999 : 0.0001);
689
+ }
690
+
691
+ return probToPrice(pos > 0 ? p0 - excessPerCtrct : p0 + excessPerCtrct);
662
692
  }
663
693
 
664
694
  /**
@@ -719,19 +749,22 @@ function pmGetDepositAmtForLvgTrade(
719
749
  S3: number,
720
750
  S2Mark: number
721
751
  ): number {
722
- const pnl = (tradeAmt * (S2Mark - price)) / S3;
723
- let p = S2Mark - 1;
724
- if (tradeAmt < 0) {
725
- p = 1 - p;
726
- }
727
- const b = (Math.abs(tradeAmt) * p) / S3 / targetLvg;
728
- const amt = -(pnl - b);
729
- // check:
730
- //bal = amt+pnl
731
- //pos_val = (np.abs(trade_amt) * p) / S3
732
- //lvg = pos_val/bal
733
- //assert(np.abs(lvg-targetLvg)<0.1)
734
- return amt;
752
+ const cmin = 0.05;
753
+ // refer to main contract function for this:
754
+ return getDepositAmountForPredMktLvgTrade(0, 0, 0, tradeAmt, targetLvg, price - 1, S3, S2Mark - 1, cmin);
755
+ // const pnl = (tradeAmt * (S2Mark - price)) / S3;
756
+ // let p = S2Mark - 1;
757
+ // if (tradeAmt < 0) {
758
+ // p = 1 - p;
759
+ // }
760
+ // const b = (Math.abs(tradeAmt) * p) / S3 / targetLvg;
761
+ // const amt = -(pnl - b);
762
+ // // check:
763
+ // //bal = amt+pnl
764
+ // //pos_val = (np.abs(trade_amt) * p) / S3
765
+ // //lvg = pos_val/bal
766
+ // //assert(np.abs(lvg-targetLvg)<0.1)
767
+ // return amt;
735
768
  }
736
769
 
737
770
  /**
@@ -763,16 +796,21 @@ function pmExcessCashAtLvg(
763
796
  Sm: number,
764
797
  S3: number
765
798
  ): number {
799
+ const cmin = 0.05;
800
+ const mu_m = 0.01;
801
+
802
+ const maxLvg = pmMaxLeverage(currentPosition + tradeAmt, Sm - 1, cmin);
803
+ lvg = lvg < maxLvg ? lvg : maxLvg;
766
804
  //determine deposit amount for given leverage
767
805
  const limitPrice = S2 * (1 + Math.sign(tradeAmt) * slippage);
768
806
  const depositFromWallet = pmGetDepositAmtForLvgTrade(tradeAmt, lvg, limitPrice, S3, Sm);
769
- const m0 = 0.18;
807
+
770
808
  //leverage fee
771
809
  let p0 = Sm - 1;
772
810
  if (tradeAmt < 0) {
773
811
  p0 = 2 - Sm; //=1-(Sm-1)
774
812
  }
775
- const feeCc = (Math.abs(tradeAmt) * pmExchangeFee(p0, m0, tradeAmt, 1 / lvg)) / S3;
813
+ const feeCc = (Math.abs(tradeAmt) * pmExchangeFee(p0, mu_m, tradeAmt, 1 / lvg)) / S3;
776
814
 
777
815
  //excess cash
778
816
  let exc = walletBalCC - depositFromWallet - feeCc;
@@ -782,12 +820,15 @@ function pmExcessCashAtLvg(
782
820
  // margin balance
783
821
  let pos = currentPosition + tradeAmt;
784
822
  let p = Sm - 1;
823
+ let entryP = limitPrice - 1;
785
824
  if (pos < 0) {
786
- p = 2 - Sm;
825
+ p = 1 - Sm;
826
+ entryP = 1 - entryP;
787
827
  }
788
- const h = entropy(p);
789
- const tau = m0 + (0.5 - m0) * h;
790
- const thresh = Math.abs(pos) * p * tau;
828
+
829
+ const mu0 = p / lvg + Math.max(0, entryP - p);
830
+ const thresh = Math.abs(pos) * mu0;
831
+
791
832
  const b0 =
792
833
  depositFromWallet +
793
834
  currentCashCC +
@@ -947,7 +988,7 @@ export function pmFindMaxPersonalTradeSizeAtLeverage(
947
988
  * @param short Short open OI
948
989
  * @param sm Mark price (>1)
949
990
  * @param isBuy True if trade is long
950
- * @param mr Maintenance margin rate
991
+ * @param mr Margin threshold per contract for liquidation (mu_m)
951
992
  */
952
993
  export function pmMaxSignedOpenTradeSize(
953
994
  long: number,
@@ -957,10 +998,12 @@ export function pmMaxSignedOpenTradeSize(
957
998
  mr: number,
958
999
  ammFundsQC: number
959
1000
  ) {
960
- if (sm < 1) {
1001
+ if (sm <= 1 || sm >= 2) {
1002
+ // closed
961
1003
  return 0;
962
1004
  }
963
- const m = pmMaintenanceMarginRate(isBuy ? 1 : -1, sm, mr);
1005
+ const counterPos = isBuy ? -short : long;
1006
+ const m = pmMaintenanceMarginRate(counterPos, counterPos * sm, sm, mr);
964
1007
  let p = !isBuy ? sm - 1 : 2 - sm;
965
1008
  p = p < 0.01 ? 0.01 : p > 0.99 ? 0.99 : p; // same cap as contract
966
1009
  return isBuy ? (ammFundsQC + m * p * short) / p - long : -(ammFundsQC + m * p * long) / p + short;
@@ -1,7 +1,7 @@
1
1
  import { BigNumberish, JsonRpcProvider, Overrides, Signer, TransactionResponse } from "ethers";
2
2
  import { PayableOverrides } from "./contracts/common";
3
3
  import { IPyth__factory } from "./contracts/factories";
4
- import { ABK64x64ToFloat, entropy, floatToABK64x64 } from "./d8XMath";
4
+ import { ABK64x64ToFloat, floatToABK64x64 } from "./d8XMath";
5
5
  import type { NodeSDKConfig, PriceFeedSubmission } from "./nodeSDKTypes";
6
6
  import WriteAccessHandler from "./writeAccessHandler";
7
7
 
@@ -282,8 +282,10 @@ export default class LiquidatorTool extends WriteAccessHandler {
282
282
  let threshold: number;
283
283
  if (this.isPredictionMarket(symbol)) {
284
284
  const idx_markPrice = 8;
285
+ const idx_lockedInValue = 5;
285
286
  const markPrice = ABK64x64ToFloat(traderState[idx_markPrice]);
286
- threshold = LiquidatorTool.maintenanceMarginPredMkts(maintMgnRate, pos, coll2quote, markPrice);
287
+ const ell = ABK64x64ToFloat(traderState[idx_lockedInValue]);
288
+ threshold = LiquidatorTool.maintenanceMarginPredMkts(maintMgnRate, pos, coll2quote, markPrice, ell);
287
289
  } else {
288
290
  const base2collateral = indexPrices[0] / coll2quote;
289
291
  threshold = Math.abs(pos * base2collateral * maintMgnRate);
@@ -291,14 +293,18 @@ export default class LiquidatorTool extends WriteAccessHandler {
291
293
  return marginbalance >= threshold;
292
294
  }
293
295
 
294
- public static maintenanceMarginPredMkts(maintMgnRateBase: number, pos: number, s3: number, markPx: number) {
295
- let p = markPx - 1;
296
- // p: price = 1+prob
297
- if (pos < 0) {
298
- p = 1 - p;
299
- }
300
- const tau = maintMgnRateBase + (0.4 - maintMgnRateBase) * entropy(p);
301
- return (Math.abs(pos) * p * tau) / s3;
296
+ public static maintenanceMarginPredMkts(
297
+ maintMgnRateBase: number,
298
+ pos: number,
299
+ s3: number,
300
+ markPx: number,
301
+ ell: number
302
+ ) {
303
+ const s = pos > 0 ? 1 : -1; // pos is
304
+ const R = pos == 0 ? markPx - 1 : s > 0 ? Math.abs(ell / pos) - 1 : 2 - Math.abs(ell / pos);
305
+ const Rm = s > 0 ? markPx - 1 : 2 - markPx;
306
+ const L = Math.max(0, s * (R - Rm));
307
+ return (Math.abs(pos) * Math.min(maintMgnRateBase + L, R)) / s3; // liquidated if E < that
302
308
  }
303
309
 
304
310
  /**