@drift-labs/sdk 2.34.1-beta.2 → 2.34.1-beta.3

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.
@@ -4,10 +4,107 @@ import {
4
4
  PRICE_PRECISION,
5
5
  QUOTE_PRECISION,
6
6
  ZERO,
7
+ ONE,
7
8
  } from '../constants/numericConstants';
8
9
  import { PerpMarketAccount, isVariant } from '../types';
9
10
  import { OraclePriceData } from '../oracles/types';
10
11
  import { calculateBidAskPrice } from './amm';
12
+ import { calculateLiveOracleTwap } from './oracles';
13
+
14
+ function calculateLiveMarkTwap(
15
+ market: PerpMarketAccount,
16
+ oraclePriceData?: OraclePriceData,
17
+ markPrice?: BN,
18
+ now?: BN,
19
+ period = new BN(3600)
20
+ ): BN {
21
+ now = now || new BN((Date.now() / 1000).toFixed(0));
22
+
23
+ const lastMarkTwapWithMantissa = market.amm.lastMarkPriceTwap;
24
+ const lastMarkPriceTwapTs = market.amm.lastMarkPriceTwapTs;
25
+
26
+ const timeSinceLastMarkChange = now.sub(lastMarkPriceTwapTs);
27
+ const markTwapTimeSinceLastUpdate = BN.max(
28
+ period,
29
+ BN.max(ZERO, period.sub(timeSinceLastMarkChange))
30
+ );
31
+
32
+ if (!markPrice) {
33
+ const [bid, ask] = calculateBidAskPrice(market.amm, oraclePriceData);
34
+ markPrice = bid.add(ask).div(new BN(2));
35
+ }
36
+
37
+ const markTwapWithMantissa = markTwapTimeSinceLastUpdate
38
+ .mul(lastMarkTwapWithMantissa)
39
+ .add(timeSinceLastMarkChange.mul(markPrice))
40
+ .div(timeSinceLastMarkChange.add(markTwapTimeSinceLastUpdate));
41
+
42
+ return markTwapWithMantissa;
43
+ }
44
+
45
+ function shrinkStaleTwaps(
46
+ market: PerpMarketAccount,
47
+ markTwapWithMantissa: BN,
48
+ oracleTwapWithMantissa: BN,
49
+ now?: BN
50
+ ) {
51
+ now = now || new BN((Date.now() / 1000).toFixed(0));
52
+ let newMarkTwap = markTwapWithMantissa;
53
+ let newOracleTwap = oracleTwapWithMantissa;
54
+ if (
55
+ market.amm.lastMarkPriceTwapTs.gt(
56
+ market.amm.historicalOracleData.lastOraclePriceTwapTs
57
+ )
58
+ ) {
59
+ // shrink oracle based on invalid intervals
60
+ const oracleInvalidDuration = BN.max(
61
+ ZERO,
62
+ market.amm.lastMarkPriceTwapTs.sub(
63
+ market.amm.historicalOracleData.lastOraclePriceTwapTs
64
+ )
65
+ );
66
+ const timeSinceLastOracleTwapUpdate = now.sub(
67
+ market.amm.historicalOracleData.lastOraclePriceTwapTs
68
+ );
69
+ const oracleTwapTimeSinceLastUpdate = BN.max(
70
+ ONE,
71
+ BN.min(
72
+ market.amm.fundingPeriod,
73
+ BN.max(ONE, market.amm.fundingPeriod.sub(timeSinceLastOracleTwapUpdate))
74
+ )
75
+ );
76
+ newOracleTwap = oracleTwapTimeSinceLastUpdate
77
+ .mul(oracleTwapWithMantissa)
78
+ .add(oracleInvalidDuration.mul(markTwapWithMantissa))
79
+ .div(oracleTwapTimeSinceLastUpdate.add(oracleInvalidDuration));
80
+ } else if (
81
+ market.amm.lastMarkPriceTwapTs.lt(
82
+ market.amm.historicalOracleData.lastOraclePriceTwapTs
83
+ )
84
+ ) {
85
+ // shrink mark to oracle twap over tradless intervals
86
+ const tradelessDuration = BN.max(
87
+ ZERO,
88
+ market.amm.historicalOracleData.lastOraclePriceTwapTs.sub(
89
+ market.amm.lastMarkPriceTwapTs
90
+ )
91
+ );
92
+ const timeSinceLastMarkTwapUpdate = now.sub(market.amm.lastMarkPriceTwapTs);
93
+ const markTwapTimeSinceLastUpdate = BN.max(
94
+ ONE,
95
+ BN.min(
96
+ market.amm.fundingPeriod,
97
+ BN.max(ONE, market.amm.fundingPeriod.sub(timeSinceLastMarkTwapUpdate))
98
+ )
99
+ );
100
+ newMarkTwap = markTwapTimeSinceLastUpdate
101
+ .mul(markTwapWithMantissa)
102
+ .add(tradelessDuration.mul(oracleTwapWithMantissa))
103
+ .div(markTwapTimeSinceLastUpdate.add(tradelessDuration));
104
+ }
105
+
106
+ return [newMarkTwap, newOracleTwap];
107
+ }
11
108
 
12
109
  /**
13
110
  *
@@ -19,110 +116,65 @@ import { calculateBidAskPrice } from './amm';
19
116
  export async function calculateAllEstimatedFundingRate(
20
117
  market: PerpMarketAccount,
21
118
  oraclePriceData?: OraclePriceData,
22
- periodAdjustment: BN = new BN(1),
119
+ markPrice?: BN,
23
120
  now?: BN
24
121
  ): Promise<[BN, BN, BN, BN, BN]> {
25
- // periodAdjustment
26
- // 1: hourly
27
- // 24: daily
28
- // 24 * 365.25: annualized
29
- const secondsInHour = new BN(3600);
30
- const hoursInDay = new BN(24);
31
- const ONE = new BN(1);
32
-
33
122
  if (isVariant(market.status, 'uninitialized')) {
34
123
  return [ZERO, ZERO, ZERO, ZERO, ZERO];
35
124
  }
36
125
 
37
- const payFreq = new BN(market.amm.fundingPeriod);
38
-
39
126
  // todo: sufficiently differs from blockchain timestamp?
40
127
  now = now || new BN((Date.now() / 1000).toFixed(0));
41
- const timeSinceLastUpdate = now.sub(market.amm.lastFundingRateTs);
42
128
 
43
- // calculate real-time mark twap
44
- const lastMarkTwapWithMantissa = market.amm.lastMarkPriceTwap;
45
- const lastMarkPriceTwapTs = market.amm.lastMarkPriceTwapTs;
46
-
47
- const timeSinceLastMarkChange = now.sub(lastMarkPriceTwapTs);
48
- const markTwapTimeSinceLastUpdate = BN.max(
49
- secondsInHour,
50
- BN.max(ZERO, secondsInHour.sub(timeSinceLastMarkChange))
129
+ // calculate real-time mark and oracle twap
130
+ const liveMarkTwap = calculateLiveMarkTwap(
131
+ market,
132
+ oraclePriceData,
133
+ markPrice,
134
+ now,
135
+ market.amm.fundingPeriod
51
136
  );
52
- const [bid, ask] = calculateBidAskPrice(market.amm, oraclePriceData);
53
- const baseAssetPriceWithMantissa = bid.add(ask).div(new BN(2));
54
-
55
- const markTwapWithMantissa = markTwapTimeSinceLastUpdate
56
- .mul(lastMarkTwapWithMantissa)
57
- .add(timeSinceLastMarkChange.mul(baseAssetPriceWithMantissa))
58
- .div(timeSinceLastMarkChange.add(markTwapTimeSinceLastUpdate));
59
-
60
- // calculate real-time (predicted) oracle twap
61
- // note: oracle twap depends on `when the chord is struck` (market is trade)
62
- const lastOracleTwapWithMantissa =
63
- market.amm.historicalOracleData.lastOraclePriceTwap;
64
- const lastOraclePriceTwapTs =
65
- market.amm.historicalOracleData.lastOraclePriceTwapTs;
66
-
67
- const oracleInvalidDuration = BN.max(
68
- ZERO,
69
- lastMarkPriceTwapTs.sub(lastOraclePriceTwapTs)
137
+ const liveOracleTwap = calculateLiveOracleTwap(
138
+ market.amm.historicalOracleData,
139
+ oraclePriceData,
140
+ now,
141
+ market.amm.fundingPeriod
70
142
  );
71
-
72
- const timeSinceLastOracleTwapUpdate = now.sub(lastOraclePriceTwapTs);
73
- const oracleTwapTimeSinceLastUpdate = BN.max(
74
- ONE,
75
- BN.min(
76
- secondsInHour,
77
- BN.max(ONE, secondsInHour.sub(timeSinceLastOracleTwapUpdate))
78
- )
143
+ const [markTwap, oracleTwap] = shrinkStaleTwaps(
144
+ market,
145
+ liveMarkTwap,
146
+ liveOracleTwap,
147
+ now
79
148
  );
80
- let oracleTwapWithMantissa = lastOracleTwapWithMantissa;
81
-
82
- // if passing live oracle data, improve predicted calc estimate
83
- if (oraclePriceData) {
84
- const oraclePrice = oraclePriceData.price;
85
149
 
86
- const oracleLiveVsTwap = oraclePrice
87
- .sub(lastOracleTwapWithMantissa)
88
- .abs()
89
- .mul(PRICE_PRECISION)
90
- .mul(new BN(100))
91
- .div(lastOracleTwapWithMantissa);
150
+ // if(!markTwap.eq(liveMarkTwap)){
151
+ // console.log('shrink mark:', liveMarkTwap.toString(), '->', markTwap.toString());
152
+ // }
92
153
 
93
- // verify pyth live input is within 20% of last twap for live update
94
- if (oracleLiveVsTwap.lte(PRICE_PRECISION.mul(new BN(20)))) {
95
- oracleTwapWithMantissa = oracleTwapTimeSinceLastUpdate
96
- .mul(lastOracleTwapWithMantissa)
97
- .add(timeSinceLastMarkChange.mul(oraclePrice))
98
- .div(timeSinceLastMarkChange.add(oracleTwapTimeSinceLastUpdate));
99
- }
100
- }
101
-
102
- const shrunkLastOracleTwapwithMantissa = oracleTwapTimeSinceLastUpdate
103
- .mul(oracleTwapWithMantissa)
104
- .add(oracleInvalidDuration.mul(lastMarkTwapWithMantissa))
105
- .div(oracleTwapTimeSinceLastUpdate.add(oracleInvalidDuration));
106
-
107
- const twapSpread = markTwapWithMantissa.sub(shrunkLastOracleTwapwithMantissa);
154
+ // if(!oracleTwap.eq(liveOracleTwap)){
155
+ // console.log('shrink orac:', liveOracleTwap.toString(), '->', oracleTwap.toString());
156
+ // }
108
157
 
158
+ const twapSpread = markTwap.sub(oracleTwap);
109
159
  const twapSpreadPct = twapSpread
110
160
  .mul(PRICE_PRECISION)
111
161
  .mul(new BN(100))
112
- .div(shrunkLastOracleTwapwithMantissa);
162
+ .div(oracleTwap);
163
+
164
+ const secondsInHour = new BN(3600);
165
+ const hoursInDay = new BN(24);
166
+ const timeSinceLastUpdate = now.sub(market.amm.lastFundingRateTs);
113
167
 
114
168
  const lowerboundEst = twapSpreadPct
115
- .mul(payFreq)
169
+ .mul(market.amm.fundingPeriod)
116
170
  .mul(BN.min(secondsInHour, timeSinceLastUpdate))
117
- .mul(periodAdjustment)
118
171
  .div(secondsInHour)
119
172
  .div(secondsInHour)
120
173
  .div(hoursInDay);
121
174
 
122
- const interpEst = twapSpreadPct.mul(periodAdjustment).div(hoursInDay);
175
+ const interpEst = twapSpreadPct.div(hoursInDay);
123
176
 
124
177
  const interpRateQuote = twapSpreadPct
125
- .mul(periodAdjustment)
126
178
  .div(hoursInDay)
127
179
  .div(PRICE_PRECISION.div(QUOTE_PRECISION));
128
180
 
@@ -140,13 +192,7 @@ export async function calculateAllEstimatedFundingRate(
140
192
  largerSide = market.amm.baseAssetAmountLong.abs();
141
193
  smallerSide = market.amm.baseAssetAmountShort.abs();
142
194
  if (twapSpread.gt(new BN(0))) {
143
- return [
144
- markTwapWithMantissa,
145
- oracleTwapWithMantissa,
146
- lowerboundEst,
147
- interpEst,
148
- interpEst,
149
- ];
195
+ return [markTwap, oracleTwap, lowerboundEst, interpEst, interpEst];
150
196
  }
151
197
  } else if (
152
198
  market.amm.baseAssetAmountLong.lt(market.amm.baseAssetAmountShort.abs())
@@ -154,22 +200,10 @@ export async function calculateAllEstimatedFundingRate(
154
200
  largerSide = market.amm.baseAssetAmountShort.abs();
155
201
  smallerSide = market.amm.baseAssetAmountLong.abs();
156
202
  if (twapSpread.lt(new BN(0))) {
157
- return [
158
- markTwapWithMantissa,
159
- oracleTwapWithMantissa,
160
- lowerboundEst,
161
- interpEst,
162
- interpEst,
163
- ];
203
+ return [markTwap, oracleTwap, lowerboundEst, interpEst, interpEst];
164
204
  }
165
205
  } else {
166
- return [
167
- markTwapWithMantissa,
168
- oracleTwapWithMantissa,
169
- lowerboundEst,
170
- interpEst,
171
- interpEst,
172
- ];
206
+ return [markTwap, oracleTwap, lowerboundEst, interpEst, interpEst];
173
207
  }
174
208
 
175
209
  if (largerSide.gt(ZERO)) {
@@ -183,8 +217,7 @@ export async function calculateAllEstimatedFundingRate(
183
217
  cappedAltEst = cappedAltEst
184
218
  .mul(PRICE_PRECISION)
185
219
  .mul(new BN(100))
186
- .div(oracleTwapWithMantissa)
187
- .mul(periodAdjustment);
220
+ .div(oracleTwap);
188
221
 
189
222
  if (cappedAltEst.abs().gte(interpEst.abs())) {
190
223
  cappedAltEst = interpEst;
@@ -193,44 +226,7 @@ export async function calculateAllEstimatedFundingRate(
193
226
  cappedAltEst = interpEst;
194
227
  }
195
228
 
196
- return [
197
- markTwapWithMantissa,
198
- oracleTwapWithMantissa,
199
- lowerboundEst,
200
- cappedAltEst,
201
- interpEst,
202
- ];
203
- }
204
-
205
- /**
206
- *
207
- * @param market
208
- * @param oraclePriceData
209
- * @param periodAdjustment
210
- * @param estimationMethod
211
- * @returns Estimated funding rate. : Precision //TODO-PRECISION
212
- */
213
- export async function calculateEstimatedFundingRate(
214
- market: PerpMarketAccount,
215
- oraclePriceData?: OraclePriceData,
216
- periodAdjustment: BN = new BN(1),
217
- estimationMethod?: 'interpolated' | 'lowerbound' | 'capped'
218
- ): Promise<BN> {
219
- const [_1, _2, lowerboundEst, cappedAltEst, interpEst] =
220
- await calculateAllEstimatedFundingRate(
221
- market,
222
- oraclePriceData,
223
- periodAdjustment
224
- );
225
-
226
- if (estimationMethod == 'lowerbound') {
227
- //assuming remaining funding period has no gap
228
- return lowerboundEst;
229
- } else if (estimationMethod == 'capped') {
230
- return cappedAltEst;
231
- } else {
232
- return interpEst;
233
- }
229
+ return [markTwap, oracleTwap, lowerboundEst, cappedAltEst, interpEst];
234
230
  }
235
231
 
236
232
  /**
@@ -243,13 +239,15 @@ export async function calculateEstimatedFundingRate(
243
239
  export async function calculateLongShortFundingRate(
244
240
  market: PerpMarketAccount,
245
241
  oraclePriceData?: OraclePriceData,
246
- periodAdjustment: BN = new BN(1)
242
+ markPrice?: BN,
243
+ now?: BN
247
244
  ): Promise<[BN, BN]> {
248
245
  const [_1, _2, _, cappedAltEst, interpEst] =
249
246
  await calculateAllEstimatedFundingRate(
250
247
  market,
251
248
  oraclePriceData,
252
- periodAdjustment
249
+ markPrice,
250
+ now
253
251
  );
254
252
 
255
253
  if (market.amm.baseAssetAmountLong.gt(market.amm.baseAssetAmountShort)) {
@@ -273,13 +271,15 @@ export async function calculateLongShortFundingRate(
273
271
  export async function calculateLongShortFundingRateAndLiveTwaps(
274
272
  market: PerpMarketAccount,
275
273
  oraclePriceData?: OraclePriceData,
276
- periodAdjustment: BN = new BN(1)
274
+ markPrice?: BN,
275
+ now?: BN
277
276
  ): Promise<[BN, BN, BN, BN]> {
278
277
  const [markTwapLive, oracleTwapLive, _2, cappedAltEst, interpEst] =
279
278
  await calculateAllEstimatedFundingRate(
280
279
  market,
281
280
  oraclePriceData,
282
- periodAdjustment
281
+ markPrice,
282
+ now
283
283
  );
284
284
 
285
285
  if (
package/src/user.ts CHANGED
@@ -1442,14 +1442,17 @@ export class User {
1442
1442
 
1443
1443
  /**
1444
1444
  * calculates max allowable leverage exceeding hitting requirement category
1445
+ * for large sizes where imf factor activates, result is a lower bound
1445
1446
  * @params category {Initial, Maintenance}
1446
1447
  * @returns : Precision TEN_THOUSAND
1447
1448
  */
1448
1449
  public getMaxLeverageForPerp(
1449
1450
  perpMarketIndex: number,
1450
- category: MarginCategory = 'Initial'
1451
+ marginCategory: MarginCategory = 'Initial'
1451
1452
  ): BN {
1452
1453
  const market = this.driftClient.getPerpMarketAccount(perpMarketIndex);
1454
+ const marketPrice =
1455
+ this.driftClient.getOracleDataForPerpMarket(perpMarketIndex).price;
1453
1456
 
1454
1457
  const { perpLiabilityValue, perpPnl, spotAssetValue, spotLiabilityValue } =
1455
1458
  this.getLeverageComponents();
@@ -1464,13 +1467,61 @@ export class User {
1464
1467
 
1465
1468
  const totalLiabilityValue = perpLiabilityValue.add(spotLiabilityValue);
1466
1469
 
1467
- const marginRatio = calculateMarketMarginRatio(
1468
- market,
1469
- ZERO, // todo
1470
- category
1471
- );
1472
1470
  const freeCollateral = this.getFreeCollateral();
1473
1471
 
1472
+ let rawMarginRatio;
1473
+
1474
+ switch (marginCategory) {
1475
+ case 'Initial':
1476
+ rawMarginRatio = market.marginRatioInitial;
1477
+ break;
1478
+ case 'Maintenance':
1479
+ rawMarginRatio = market.marginRatioMaintenance;
1480
+ break;
1481
+ default:
1482
+ rawMarginRatio = market.marginRatioInitial;
1483
+ break;
1484
+ }
1485
+
1486
+ // absolute max fesible size (upper bound)
1487
+ const maxSize = BN.max(
1488
+ ZERO,
1489
+ freeCollateral
1490
+ .mul(MARGIN_PRECISION)
1491
+ .div(new BN(rawMarginRatio))
1492
+ .mul(PRICE_PRECISION)
1493
+ .div(marketPrice)
1494
+ );
1495
+
1496
+ // margin ratio incorporting upper bound on size
1497
+ let marginRatio = calculateMarketMarginRatio(
1498
+ market,
1499
+ maxSize,
1500
+ marginCategory
1501
+ );
1502
+
1503
+ // use more fesible size since imf factor activated
1504
+ let attempts = 0;
1505
+ while (marginRatio > rawMarginRatio + 1e-4 && attempts < 10) {
1506
+ // more fesible size (upper bound)
1507
+ const targetSize = BN.max(
1508
+ ZERO,
1509
+ freeCollateral
1510
+ .mul(MARGIN_PRECISION)
1511
+ .div(new BN(marginRatio))
1512
+ .mul(PRICE_PRECISION)
1513
+ .div(marketPrice)
1514
+ );
1515
+
1516
+ // margin ratio incorporting more fesible target size
1517
+ marginRatio = calculateMarketMarginRatio(
1518
+ market,
1519
+ targetSize,
1520
+ marginCategory
1521
+ );
1522
+ attempts += 1;
1523
+ }
1524
+
1474
1525
  // how much more liabilities can be opened w remaining free collateral
1475
1526
  const additionalLiabilities = freeCollateral
1476
1527
  .mul(MARGIN_PRECISION)
package/tests/amm/test.ts CHANGED
@@ -10,10 +10,14 @@ import {
10
10
  calculateLiveOracleStd,
11
11
  calculateLiveOracleTwap,
12
12
  calculateInventoryScale,
13
+ calculateAllEstimatedFundingRate,
14
+ calculateLongShortFundingRateAndLiveTwaps,
15
+ OraclePriceData
13
16
  } from '../../src';
14
17
  import { mockPerpMarkets } from '../dlob/helpers';
15
18
 
16
19
  import { assert } from '../../src/assert/assert';
20
+ import * as _ from 'lodash';
17
21
 
18
22
  class AMMSpreadTerms {
19
23
  longVolSpread: number;
@@ -386,4 +390,122 @@ describe('AMM Tests', () => {
386
390
  console.log('liveOracleStd:', liveOracleStd.toNumber());
387
391
  assert(liveOracleStd.eq(new BN(192962)));
388
392
  });
393
+
394
+ it('predicted funding rate mock1', async () => {
395
+ const myMockPerpMarkets = _.cloneDeep(mockPerpMarkets);
396
+ const mockMarket1 = myMockPerpMarkets[0];
397
+
398
+ // make it like RNDR
399
+ const now = new BN(1688878353);
400
+
401
+ mockMarket1.amm.fundingPeriod = new BN(3600);
402
+ mockMarket1.amm.lastFundingRateTs = new BN(1688860817);
403
+
404
+ const currentMarkPrice = new BN(1.9843 * PRICE_PRECISION.toNumber()); // trading at a premium
405
+ const oraclePriceData: OraclePriceData = {
406
+ price: new BN(1.9535 * PRICE_PRECISION.toNumber()),
407
+ slot: new BN(0),
408
+ confidence: new BN(1),
409
+ hasSufficientNumberOfDataPoints: true,
410
+ };
411
+ mockMarket1.amm.historicalOracleData.lastOraclePrice = new BN(1.9535 * PRICE_PRECISION.toNumber());
412
+
413
+ // mockMarket1.amm.pegMultiplier = new BN(1.897573 * 1e3);
414
+
415
+ mockMarket1.amm.lastMarkPriceTwap = new BN(1.945594 * PRICE_PRECISION.toNumber());
416
+ mockMarket1.amm.lastBidPriceTwap = new BN(1.941629 * PRICE_PRECISION.toNumber());
417
+ mockMarket1.amm.lastAskPriceTwap = new BN(1.94956 * PRICE_PRECISION.toNumber());
418
+ mockMarket1.amm.lastMarkPriceTwapTs = new BN(1688877729);
419
+
420
+ mockMarket1.amm.historicalOracleData.lastOraclePriceTwap = new BN(1.942449 * PRICE_PRECISION.toNumber());
421
+ mockMarket1.amm.historicalOracleData.lastOraclePriceTwapTs = new BN(1688878333);
422
+
423
+ const [_markTwapLive, _oracleTwapLive, _lowerboundEst, _cappedAltEst, _interpEst] =
424
+ await calculateAllEstimatedFundingRate(
425
+ mockMarket1,
426
+ oraclePriceData,
427
+ currentMarkPrice,
428
+ now
429
+ );
430
+
431
+ const [markTwapLive, oracleTwapLive, est1, est2] = await calculateLongShortFundingRateAndLiveTwaps(
432
+ mockMarket1,
433
+ oraclePriceData,
434
+ currentMarkPrice,
435
+ now
436
+ );
437
+
438
+ // console.log(markTwapLive.toString());
439
+ // console.log(oracleTwapLive.toString());
440
+ // console.log(est1.toString());
441
+ // console.log(est2.toString());
442
+
443
+ assert(markTwapLive.eq(new BN('1949826')));
444
+ assert(oracleTwapLive.eq(new BN('1942510')));
445
+ assert(est1.eq(new BN('15692')));
446
+ assert(est2.eq(new BN('15692')));
447
+
448
+ });
449
+
450
+ it('predicted funding rate mock2', async () => {
451
+ const myMockPerpMarkets = _.cloneDeep(mockPerpMarkets);
452
+ const mockMarket1 = myMockPerpMarkets[0];
453
+
454
+ // make it like OP
455
+ const now = new BN(1688881915);
456
+
457
+ mockMarket1.amm.fundingPeriod = new BN(3600);
458
+ mockMarket1.amm.lastFundingRateTs = new BN(1688864415);
459
+
460
+ const currentMarkPrice = new BN(1.2242 * PRICE_PRECISION.toNumber()); // trading at a premium
461
+ const oraclePriceData: OraclePriceData = {
462
+ price: new BN(1.224 * PRICE_PRECISION.toNumber()),
463
+ slot: new BN(0),
464
+ confidence: new BN(1),
465
+ hasSufficientNumberOfDataPoints: true,
466
+ };
467
+ mockMarket1.amm.historicalOracleData.lastOraclePrice = new BN(1.9535 * PRICE_PRECISION.toNumber());
468
+
469
+ // mockMarket1.amm.pegMultiplier = new BN(1.897573 * 1e3);
470
+
471
+ mockMarket1.amm.lastMarkPriceTwap = new BN(1.218363 * PRICE_PRECISION.toNumber());
472
+ mockMarket1.amm.lastBidPriceTwap = new BN(1.218363 * PRICE_PRECISION.toNumber());
473
+ mockMarket1.amm.lastAskPriceTwap = new BN(1.218364 * PRICE_PRECISION.toNumber());
474
+ mockMarket1.amm.lastMarkPriceTwapTs = new BN(1688878815);
475
+
476
+ mockMarket1.amm.historicalOracleData.lastOraclePriceTwap = new BN(1.220964 * PRICE_PRECISION.toNumber());
477
+ mockMarket1.amm.historicalOracleData.lastOraclePriceTwapTs = new BN(1688879991);
478
+
479
+ const [_markTwapLive, _oracleTwapLive, _lowerboundEst, _cappedAltEst, _interpEst] =
480
+ await calculateAllEstimatedFundingRate(
481
+ mockMarket1,
482
+ oraclePriceData,
483
+ currentMarkPrice,
484
+ now
485
+ );
486
+
487
+ // console.log(_markTwapLive.toString());
488
+ // console.log(_oracleTwapLive.toString());
489
+ // console.log(_lowerboundEst.toString());
490
+ // console.log(_cappedAltEst.toString());
491
+ // console.log(_interpEst.toString());
492
+ // console.log('-----');
493
+
494
+ const [markTwapLive, oracleTwapLive, est1, est2] = await calculateLongShortFundingRateAndLiveTwaps(
495
+ mockMarket1,
496
+ oraclePriceData,
497
+ currentMarkPrice,
498
+ now
499
+ );
500
+
501
+ console.log('markTwapLive:', mockMarket1.amm.lastMarkPriceTwap.toString(), '->', markTwapLive.toString());
502
+ console.log('oracTwapLive:', mockMarket1.amm.historicalOracleData.lastOraclePriceTwap.toString(), '->', oracleTwapLive.toString());
503
+ console.log('pred funding:', est1.toString(), est2.toString());
504
+
505
+ assert(markTwapLive.eq(new BN('1222131')));
506
+ assert(oracleTwapLive.eq(new BN('1222586')));
507
+ assert(est1.eq(est2));
508
+ assert(est2.eq(new BN('-1550')));
509
+
510
+ });
389
511
  });
@@ -21,6 +21,7 @@ import {
21
21
  OrderRecord,
22
22
  ZERO,
23
23
  ContractTier,
24
+ SPOT_MARKET_BALANCE_PRECISION,
24
25
  } from '../../src';
25
26
 
26
27
  export const mockPerpPosition: PerpPosition = {
@@ -151,8 +152,8 @@ export const mockPerpMarkets: Array<PerpMarketAccount> = [
151
152
  amm: mockAMM,
152
153
  numberOfUsersWithBase: 0,
153
154
  numberOfUsers: 0,
154
- marginRatioInitial: 0,
155
- marginRatioMaintenance: 0,
155
+ marginRatioInitial: 2000,
156
+ marginRatioMaintenance: 1000,
156
157
  nextFillRecordId: new BN(0),
157
158
  pnlPool: {
158
159
  scaledBalance: new BN(0),
@@ -257,7 +258,7 @@ export const mockSpotMarkets: Array<SpotMarketAccount> = [
257
258
  status: MarketStatus.ACTIVE,
258
259
  assetTier: AssetTier.COLLATERAL,
259
260
  name: [],
260
- maxTokenDeposits: new BN(100),
261
+ maxTokenDeposits: new BN(1000000),
261
262
  marketIndex: 0,
262
263
  pubkey: PublicKey.default,
263
264
  mint: DevnetSpotMarkets[0].mint,
@@ -285,8 +286,8 @@ export const mockSpotMarkets: Array<SpotMarketAccount> = [
285
286
  optimalUtilization: 0,
286
287
  optimalBorrowRate: 0,
287
288
  maxBorrowRate: 0,
288
- cumulativeDepositInterest: new BN(0),
289
- cumulativeBorrowInterest: new BN(0),
289
+ cumulativeDepositInterest: SPOT_MARKET_BALANCE_PRECISION,
290
+ cumulativeBorrowInterest: SPOT_MARKET_BALANCE_PRECISION,
290
291
  totalSocialLoss: new BN(0),
291
292
  totalQuoteSocialLoss: new BN(0),
292
293
  depositBalance: new BN(0),