@drift-labs/sdk 0.2.0-master.4 → 0.2.0-temp.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.
package/src/math/amm.ts CHANGED
@@ -68,7 +68,6 @@ export function calculateOptimalPegAndBudget(
68
68
  let newOptimalPeg: BN;
69
69
  let newBudget: BN;
70
70
  const targetPriceGap = markPriceBefore.sub(targetPrice);
71
-
72
71
  if (targetPriceGap.abs().gt(maxPriceSpread)) {
73
72
  const markAdj = targetPriceGap.abs().sub(maxPriceSpread);
74
73
 
@@ -412,8 +411,10 @@ export function calculateSpread(
412
411
  direction: PositionDirection,
413
412
  oraclePriceData: OraclePriceData
414
413
  ): number {
414
+ let spread = amm.baseSpread / 2;
415
+
415
416
  if (amm.baseSpread == 0 || amm.curveUpdateIntensity == 0) {
416
- return amm.baseSpread / 2;
417
+ return spread;
417
418
  }
418
419
 
419
420
  const markPrice = calculatePrice(
@@ -434,25 +435,54 @@ export function calculateSpread(
434
435
  .mul(BID_ASK_SPREAD_PRECISION)
435
436
  .div(markPrice);
436
437
 
437
- const [longSpread, shortSpread] = calculateSpreadBN(
438
- amm.baseSpread,
439
- targetMarkSpreadPct,
440
- confIntervalPct,
441
- amm.maxSpread,
442
- amm.quoteAssetReserve,
443
- amm.terminalQuoteAssetReserve,
444
- amm.pegMultiplier,
445
- amm.netBaseAssetAmount,
446
- markPrice,
447
- amm.totalFeeMinusDistributions
448
- );
438
+ // oracle retreat
439
+ if (
440
+ (isVariant(direction, 'long') && targetMarkSpreadPct.lt(ZERO)) ||
441
+ (isVariant(direction, 'short') && targetMarkSpreadPct.gt(ZERO))
442
+ ) {
443
+ spread = Math.max(
444
+ spread,
445
+ targetMarkSpreadPct.abs().toNumber() + confIntervalPct.abs().toNumber()
446
+ );
447
+ }
449
448
 
450
- let spread: number;
449
+ // inventory skew
450
+ const MAX_INVENTORY_SKEW = 5;
451
+ if (
452
+ (amm.netBaseAssetAmount.gt(ZERO) && isVariant(direction, 'long')) ||
453
+ (amm.netBaseAssetAmount.lt(ZERO) && isVariant(direction, 'short')) ||
454
+ amm.totalFeeMinusDistributions.eq(ZERO)
455
+ ) {
456
+ const netCostBasis = amm.quoteAssetAmountLong.sub(
457
+ amm.quoteAssetAmountShort
458
+ );
459
+ const netBaseAssetValue = amm.quoteAssetReserve
460
+ .sub(amm.terminalQuoteAssetReserve)
461
+ .mul(amm.pegMultiplier)
462
+ .div(AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO);
463
+
464
+ const localBaseAssetValue = amm.netBaseAssetAmount
465
+ .mul(markPrice)
466
+ .div(AMM_TO_QUOTE_PRECISION_RATIO.mul(MARK_PRICE_PRECISION));
467
+ const netPnl = netBaseAssetValue.sub(netCostBasis);
468
+ const localPnl = localBaseAssetValue.sub(netCostBasis);
469
+
470
+ let effectiveLeverage = MAX_INVENTORY_SKEW;
471
+ if (amm.totalFeeMinusDistributions.gt(ZERO)) {
472
+ effectiveLeverage =
473
+ localPnl.sub(netPnl).toNumber() /
474
+ (amm.totalFeeMinusDistributions.toNumber() + 1);
475
+ }
451
476
 
452
- if (isVariant(direction, 'long')) {
453
- spread = longSpread;
454
- } else {
455
- spread = shortSpread;
477
+ let spreadScale = Math.min(MAX_INVENTORY_SKEW, 1 + effectiveLeverage);
478
+ const maxTargetSpread = BID_ASK_SPREAD_PRECISION.toNumber() / 50; // 2%
479
+ // cap the scale to attempt to only scale up to maxTargetSpread
480
+ // always let the oracle retreat methods go through 100%
481
+ if (spreadScale * spread > maxTargetSpread) {
482
+ spreadScale = Math.max(1.05, maxTargetSpread / spread);
483
+ }
484
+
485
+ spread *= spreadScale;
456
486
  }
457
487
 
458
488
  return spread;
@@ -2,11 +2,7 @@ import { isVariant, Order } from '../types';
2
2
  import { BN, ZERO } from '../.';
3
3
 
4
4
  export function isAuctionComplete(order: Order, slot: number): boolean {
5
- if (order.auctionDuration === 0) {
6
- return true;
7
- }
8
-
9
- return new BN(slot).sub(order.slot).gt(new BN(order.auctionDuration));
5
+ return new BN(slot).sub(order.slot).gte(new BN(order.auctionDuration));
10
6
  }
11
7
 
12
8
  export function getAuctionPrice(order: Order, slot: number): BN {
@@ -1,10 +1,9 @@
1
1
  import { ClearingHouseUser } from '../clearingHouseUser';
2
- import { isOneOfVariant, isVariant, MarketAccount, Order } from '../types';
2
+ import { isOneOfVariant, isVariant, Order } from '../types';
3
3
  import { ZERO, TWO } from '../constants/numericConstants';
4
4
  import { BN } from '@project-serum/anchor';
5
5
  import { OraclePriceData } from '../oracles/types';
6
- import { getAuctionPrice, isAuctionComplete } from './auction';
7
- import { calculateAskPrice, calculateBidPrice } from './market';
6
+ import { getAuctionPrice } from './auction';
8
7
 
9
8
  export function isOrderRiskIncreasing(
10
9
  user: ClearingHouseUser,
@@ -121,7 +120,6 @@ export function standardizeBaseAssetAmount(
121
120
 
122
121
  export function getLimitPrice(
123
122
  order: Order,
124
- market: MarketAccount,
125
123
  oraclePriceData: OraclePriceData,
126
124
  slot: number
127
125
  ): BN {
@@ -129,19 +127,7 @@ export function getLimitPrice(
129
127
  if (!order.oraclePriceOffset.eq(ZERO)) {
130
128
  limitPrice = oraclePriceData.price.add(order.oraclePriceOffset);
131
129
  } else if (isOneOfVariant(order.orderType, ['market', 'triggerMarket'])) {
132
- if (isAuctionComplete(order, slot)) {
133
- limitPrice = getAuctionPrice(order, slot);
134
- } else if (!order.price.eq(ZERO)) {
135
- limitPrice = order.price;
136
- } else if (isVariant(order.direction, 'long')) {
137
- const askPrice = calculateAskPrice(market, oraclePriceData);
138
- const delta = askPrice.div(new BN(market.amm.maxSlippageRatio));
139
- limitPrice = askPrice.add(delta);
140
- } else {
141
- const bidPrice = calculateBidPrice(market, oraclePriceData);
142
- const delta = bidPrice.div(new BN(market.amm.maxSlippageRatio));
143
- limitPrice = bidPrice.sub(delta);
144
- }
130
+ limitPrice = getAuctionPrice(order, slot);
145
131
  } else {
146
132
  limitPrice = order.price;
147
133
  }
package/src/math/trade.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { MarketAccount, PositionDirection } from '../types';
1
+ import { MarketAccount, PositionDirection, SwapDirection } from '../types';
2
2
  import { BN } from '@project-serum/anchor';
3
3
  import { assert } from '../assert/assert';
4
4
  import {
@@ -78,20 +78,28 @@ export function calculateTradeSlippage(
78
78
  if (amount.eq(ZERO)) {
79
79
  return [ZERO, ZERO, oldPrice, oldPrice];
80
80
  }
81
- const [acquiredBaseReserve, acquiredQuoteReserve, acquiredQuoteAssetAmount] =
82
- calculateTradeAcquiredAmounts(
83
- direction,
84
- amount,
85
- market,
86
- inputAssetType,
87
- oraclePriceData,
88
- useSpread
89
- );
81
+ const [acquiredBase, acquiredQuote] = calculateTradeAcquiredAmounts(
82
+ direction,
83
+ amount,
84
+ market,
85
+ inputAssetType,
86
+ oraclePriceData,
87
+ useSpread
88
+ );
89
+
90
+ const swapDirection = isVariant(direction, 'long')
91
+ ? SwapDirection.REMOVE
92
+ : SwapDirection.ADD;
93
+ const quoteAssetAmountAcquired = calculateQuoteAssetAmountSwapped(
94
+ acquiredQuote.abs(),
95
+ market.amm.pegMultiplier,
96
+ swapDirection
97
+ );
90
98
 
91
- const entryPrice = acquiredQuoteAssetAmount
99
+ const entryPrice = quoteAssetAmountAcquired
92
100
  .mul(AMM_TO_QUOTE_PRECISION_RATIO)
93
101
  .mul(MARK_PRICE_PRECISION)
94
- .div(acquiredBaseReserve.abs());
102
+ .div(acquiredBase.abs());
95
103
 
96
104
  let amm: Parameters<typeof calculateAmmReservesAfterSwap>[0];
97
105
  if (useSpread && market.amm.baseSpread > 0) {
@@ -108,8 +116,8 @@ export function calculateTradeSlippage(
108
116
  }
109
117
 
110
118
  const newPrice = calculatePrice(
111
- amm.baseAssetReserve.sub(acquiredBaseReserve),
112
- amm.quoteAssetReserve.sub(acquiredQuoteReserve),
119
+ amm.baseAssetReserve.sub(acquiredBase),
120
+ amm.quoteAssetReserve.sub(acquiredQuote),
113
121
  amm.pegMultiplier
114
122
  );
115
123
 
@@ -151,9 +159,9 @@ export function calculateTradeAcquiredAmounts(
151
159
  inputAssetType: AssetType = 'quote',
152
160
  oraclePriceData: OraclePriceData,
153
161
  useSpread = true
154
- ): [BN, BN, BN] {
162
+ ): [BN, BN] {
155
163
  if (amount.eq(ZERO)) {
156
- return [ZERO, ZERO, ZERO];
164
+ return [ZERO, ZERO];
157
165
  }
158
166
 
159
167
  const swapDirection = getSwapDirection(inputAssetType, direction);
@@ -177,13 +185,7 @@ export function calculateTradeAcquiredAmounts(
177
185
 
178
186
  const acquiredBase = amm.baseAssetReserve.sub(newBaseAssetReserve);
179
187
  const acquiredQuote = amm.quoteAssetReserve.sub(newQuoteAssetReserve);
180
- const acquiredQuoteAssetamount = calculateQuoteAssetAmountSwapped(
181
- acquiredQuote.abs(),
182
- amm.pegMultiplier,
183
- swapDirection
184
- );
185
-
186
- return [acquiredBase, acquiredQuote, acquiredQuoteAssetamount];
188
+ return [acquiredBase, acquiredQuote];
187
189
  }
188
190
 
189
191
  /**
@@ -1,33 +1,154 @@
1
- import { OptionalOrderParams, OrderTriggerCondition, OrderType } from './types';
1
+ import {
2
+ OrderParams,
3
+ OrderTriggerCondition,
4
+ OrderType,
5
+ PositionDirection,
6
+ } from './types';
2
7
  import { BN } from '@project-serum/anchor';
8
+ import { ZERO } from './constants/numericConstants';
3
9
 
4
10
  export function getLimitOrderParams(
5
- params: Omit<OptionalOrderParams, 'orderType'> & { price: BN }
6
- ): OptionalOrderParams {
7
- return Object.assign({}, params, { orderType: OrderType.LIMIT });
11
+ marketIndex: BN,
12
+ direction: PositionDirection,
13
+ baseAssetAmount: BN,
14
+ price: BN,
15
+ reduceOnly: boolean,
16
+ discountToken = false,
17
+ referrer = false,
18
+ userOrderId = 0,
19
+ postOnly = false,
20
+ oraclePriceOffset = ZERO,
21
+ immediateOrCancel = false
22
+ ): OrderParams {
23
+ return {
24
+ orderType: OrderType.LIMIT,
25
+ userOrderId,
26
+ marketIndex,
27
+ direction,
28
+ quoteAssetAmount: ZERO,
29
+ baseAssetAmount,
30
+ price,
31
+ reduceOnly,
32
+ postOnly,
33
+ immediateOrCancel,
34
+ positionLimit: ZERO,
35
+ padding0: true,
36
+ padding1: ZERO,
37
+ optionalAccounts: {
38
+ discountToken,
39
+ referrer,
40
+ },
41
+ triggerCondition: OrderTriggerCondition.ABOVE,
42
+ triggerPrice: ZERO,
43
+ oraclePriceOffset,
44
+ };
8
45
  }
9
46
 
10
47
  export function getTriggerMarketOrderParams(
11
- params: Omit<OptionalOrderParams, 'orderType'> & {
12
- triggerCondition: OrderTriggerCondition;
13
- triggerPrice: BN;
14
- }
15
- ): OptionalOrderParams {
16
- return Object.assign({}, params, { orderType: OrderType.TRIGGER_MARKET });
48
+ marketIndex: BN,
49
+ direction: PositionDirection,
50
+ baseAssetAmount: BN,
51
+ triggerPrice: BN,
52
+ triggerCondition: OrderTriggerCondition,
53
+ reduceOnly: boolean,
54
+ discountToken = false,
55
+ referrer = false,
56
+ userOrderId = 0
57
+ ): OrderParams {
58
+ return {
59
+ orderType: OrderType.TRIGGER_MARKET,
60
+ userOrderId,
61
+ marketIndex,
62
+ direction,
63
+ quoteAssetAmount: ZERO,
64
+ baseAssetAmount,
65
+ price: ZERO,
66
+ reduceOnly,
67
+ postOnly: false,
68
+ immediateOrCancel: false,
69
+ positionLimit: ZERO,
70
+ padding0: true,
71
+ padding1: ZERO,
72
+ optionalAccounts: {
73
+ discountToken,
74
+ referrer,
75
+ },
76
+ triggerCondition,
77
+ triggerPrice,
78
+ oraclePriceOffset: ZERO,
79
+ };
17
80
  }
18
81
 
19
82
  export function getTriggerLimitOrderParams(
20
- params: Omit<OptionalOrderParams, 'orderType'> & {
21
- triggerCondition: OrderTriggerCondition;
22
- triggerPrice: BN;
23
- price: BN;
24
- }
25
- ): OptionalOrderParams {
26
- return Object.assign({}, params, { orderType: OrderType.TRIGGER_LIMIT });
83
+ marketIndex: BN,
84
+ direction: PositionDirection,
85
+ baseAssetAmount: BN,
86
+ price: BN,
87
+ triggerPrice: BN,
88
+ triggerCondition: OrderTriggerCondition,
89
+ reduceOnly: boolean,
90
+ discountToken = false,
91
+ referrer = false,
92
+ userOrderId = 0
93
+ ): OrderParams {
94
+ return {
95
+ orderType: OrderType.TRIGGER_LIMIT,
96
+ userOrderId,
97
+ marketIndex,
98
+ direction,
99
+ quoteAssetAmount: ZERO,
100
+ baseAssetAmount,
101
+ price,
102
+ reduceOnly,
103
+ postOnly: false,
104
+ immediateOrCancel: false,
105
+ positionLimit: ZERO,
106
+ padding0: true,
107
+ padding1: ZERO,
108
+ optionalAccounts: {
109
+ discountToken,
110
+ referrer,
111
+ },
112
+ triggerCondition,
113
+ triggerPrice,
114
+ oraclePriceOffset: ZERO,
115
+ };
27
116
  }
28
117
 
29
118
  export function getMarketOrderParams(
30
- params: Omit<OptionalOrderParams, 'orderType'>
31
- ): OptionalOrderParams {
32
- return Object.assign({}, params, { orderType: OrderType.MARKET });
119
+ marketIndex: BN,
120
+ direction: PositionDirection,
121
+ quoteAssetAmount: BN,
122
+ baseAssetAmount: BN,
123
+ reduceOnly: boolean,
124
+ price = ZERO,
125
+ discountToken = false,
126
+ referrer = false
127
+ ): OrderParams {
128
+ if (baseAssetAmount.eq(ZERO) && quoteAssetAmount.eq(ZERO)) {
129
+ throw Error('baseAssetAmount or quoteAssetAmount must be zero');
130
+ }
131
+
132
+ return {
133
+ orderType: OrderType.MARKET,
134
+ userOrderId: 0,
135
+ marketIndex,
136
+ direction,
137
+ quoteAssetAmount,
138
+ baseAssetAmount,
139
+ price,
140
+ reduceOnly,
141
+ postOnly: false,
142
+ immediateOrCancel: false,
143
+ positionLimit: ZERO,
144
+ padding0: true,
145
+ padding1: ZERO,
146
+ optionalAccounts: {
147
+ discountToken,
148
+ referrer,
149
+ },
150
+ triggerCondition: OrderTriggerCondition.ABOVE,
151
+ triggerPrice: ZERO,
152
+ oraclePriceOffset: ZERO,
153
+ };
33
154
  }
package/src/types.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { PublicKey, Transaction } from '@solana/web3.js';
2
- import { BN, ZERO } from '.';
2
+ import { BN } from '.';
3
3
 
4
4
  // # Utility Types / Enums / Constants
5
5
  export class SwapDirection {
@@ -51,19 +51,6 @@ export class OrderAction {
51
51
  static readonly TRIGGER = { trigger: {} };
52
52
  }
53
53
 
54
- export class OrderActionExplanation {
55
- static readonly NONE = { none: {} };
56
- static readonly BREACHED_MARGIN_REQUIREMENT = {
57
- breachedMarginRequirement: {},
58
- };
59
- static readonly ORACLE_PRICE_BREACHED_LIMIT_PRICE = {
60
- oraclePriceBreachedLimitPrice: {},
61
- };
62
- static readonly MARKET_ORDER_FILLED_TO_LIMIT_PRICE = {
63
- marketOrderFilledToLimitPrice: {},
64
- };
65
- }
66
-
67
54
  export class OrderTriggerCondition {
68
55
  static readonly ABOVE = { above: {} };
69
56
  static readonly BELOW = { below: {} };
@@ -177,10 +164,7 @@ export type OrderRecord = {
177
164
  maker: PublicKey;
178
165
  takerOrder: Order;
179
166
  makerOrder: Order;
180
- takerUnsettledPnl: BN;
181
- makerUnsettledPnl: BN;
182
167
  action: OrderAction;
183
- actionExplanation: OrderActionExplanation;
184
168
  filler: PublicKey;
185
169
  fillRecordId: BN;
186
170
  marketIndex: BN;
@@ -277,8 +261,6 @@ export type AMM = {
277
261
  lastMarkPriceTwapTs: BN;
278
262
  lastOraclePriceTwap: BN;
279
263
  lastOraclePriceTwapTs: BN;
280
- lastOracleMarkSpreadPct: BN;
281
- lastOracleConfPct: BN;
282
264
  oracle: PublicKey;
283
265
  oracleSource: OracleSource;
284
266
  fundingPeriod: BN;
@@ -293,8 +275,6 @@ export type AMM = {
293
275
  totalFee: BN;
294
276
  minimumQuoteAssetTradeSize: BN;
295
277
  baseAssetAmountStepSize: BN;
296
- maxBaseAssetAmountRatio: number;
297
- maxSlippageRatio: number;
298
278
  lastOraclePrice: BN;
299
279
  baseSpread: number;
300
280
  curveUpdateIntensity: number;
@@ -385,6 +365,7 @@ export type OrderParams = {
385
365
  orderType: OrderType;
386
366
  userOrderId: number;
387
367
  direction: PositionDirection;
368
+ quoteAssetAmount: BN;
388
369
  baseAssetAmount: BN;
389
370
  price: BN;
390
371
  marketIndex: BN;
@@ -403,39 +384,6 @@ export type OrderParams = {
403
384
  };
404
385
  };
405
386
 
406
- export type NecessaryOrderParams = {
407
- orderType: OrderType;
408
- marketIndex: BN;
409
- baseAssetAmount: BN;
410
- direction: PositionDirection;
411
- };
412
-
413
- export type OptionalOrderParams = {
414
- [Property in keyof OrderParams]?: OrderParams[Property];
415
- } & NecessaryOrderParams;
416
-
417
- export const DefaultOrderParams = {
418
- orderType: OrderType.MARKET,
419
- userOrderId: 0,
420
- direction: PositionDirection.LONG,
421
- baseAssetAmount: ZERO,
422
- price: ZERO,
423
- marketIndex: ZERO,
424
- reduceOnly: false,
425
- postOnly: false,
426
- immediateOrCancel: false,
427
- triggerPrice: ZERO,
428
- triggerCondition: OrderTriggerCondition.ABOVE,
429
- positionLimit: ZERO,
430
- oraclePriceOffset: ZERO,
431
- padding0: ZERO,
432
- padding1: ZERO,
433
- optionalAccounts: {
434
- discountToken: false,
435
- referrer: false,
436
- },
437
- };
438
-
439
387
  export type MakerInfo = {
440
388
  maker: PublicKey;
441
389
  order: Order;