@drift-labs/sdk 0.1.36-master.5 → 0.1.36-master.6

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.
@@ -16,6 +16,7 @@ export declare class BulkAccountLoader {
16
16
  loadPromise?: Promise<void>;
17
17
  loadPromiseResolver: () => void;
18
18
  lastTimeLoadingPromiseCleared: number;
19
+ mostRecentSlot: number;
19
20
  constructor(connection: Connection, commitment: Commitment, pollingFrequency: number);
20
21
  addAccount(publicKey: PublicKey, callback: (buffer: Buffer) => void): string;
21
22
  removeAccount(publicKey: PublicKey, callbackId: string): void;
@@ -20,6 +20,7 @@ class BulkAccountLoader {
20
20
  this.accountData = new Map();
21
21
  this.errorCallbacks = new Map();
22
22
  this.lastTimeLoadingPromiseCleared = Date.now();
23
+ this.mostRecentSlot = 0;
23
24
  this.connection = connection;
24
25
  this.commitment = commitment;
25
26
  this.pollingFrequency = pollingFrequency;
@@ -127,6 +128,9 @@ class BulkAccountLoader {
127
128
  return;
128
129
  }
129
130
  const newSlot = rpcResponse.result.context.slot;
131
+ if (newSlot > this.mostRecentSlot) {
132
+ this.mostRecentSlot = newSlot;
133
+ }
130
134
  for (const i in accountsToLoad) {
131
135
  const accountToLoad = accountsToLoad[i];
132
136
  const key = accountToLoad.publicKey.toString();
package/lib/index.d.ts CHANGED
@@ -25,6 +25,7 @@ export * from './math/funding';
25
25
  export * from './math/insuranceFund';
26
26
  export * from './math/market';
27
27
  export * from './math/position';
28
+ export * from './math/oracles';
28
29
  export * from './math/amm';
29
30
  export * from './math/trade';
30
31
  export * from './math/orders';
package/lib/index.js CHANGED
@@ -44,6 +44,7 @@ __exportStar(require("./math/funding"), exports);
44
44
  __exportStar(require("./math/insuranceFund"), exports);
45
45
  __exportStar(require("./math/market"), exports);
46
46
  __exportStar(require("./math/position"), exports);
47
+ __exportStar(require("./math/oracles"), exports);
47
48
  __exportStar(require("./math/amm"), exports);
48
49
  __exportStar(require("./math/trade"), exports);
49
50
  __exportStar(require("./math/orders"), exports);
@@ -0,0 +1,3 @@
1
+ import { AMM, OracleGuardRails } from '../types';
2
+ import { OraclePriceData } from '../oracles/types';
3
+ export declare function isOracleValid(amm: AMM, oraclePriceData: OraclePriceData, oracleGuardRails: OracleGuardRails, slot: number): boolean;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isOracleValid = void 0;
4
+ const numericConstants_1 = require("../constants/numericConstants");
5
+ const index_1 = require("../index");
6
+ function isOracleValid(amm, oraclePriceData, oracleGuardRails, slot) {
7
+ const isOraclePriceNonPositive = oraclePriceData.price.lt(numericConstants_1.ZERO);
8
+ const isOraclePriceTooVolatile = oraclePriceData.price
9
+ .div(index_1.BN.max(numericConstants_1.ONE, amm.lastOraclePriceTwap))
10
+ .gt(oracleGuardRails.validity.tooVolatileRatio) ||
11
+ amm.lastOraclePriceTwap
12
+ .div(index_1.BN.max(numericConstants_1.ONE, oraclePriceData.price))
13
+ .gt(oracleGuardRails.validity.tooVolatileRatio);
14
+ const isConfidenceTooLarge = oraclePriceData.price
15
+ .div(index_1.BN.max(numericConstants_1.ONE, oraclePriceData.confidence))
16
+ .lt(oracleGuardRails.validity.confidenceIntervalMaxSize);
17
+ const oracleIsStale = oraclePriceData.slot
18
+ .sub(new index_1.BN(slot))
19
+ .gt(oracleGuardRails.validity.slotsBeforeStale);
20
+ return !(!oraclePriceData.hasSufficientNumberOfDataPoints ||
21
+ oracleIsStale ||
22
+ isOraclePriceNonPositive ||
23
+ isOraclePriceTooVolatile ||
24
+ isConfidenceTooLarge);
25
+ }
26
+ exports.isOracleValid = isOracleValid;
@@ -8,6 +8,8 @@ export declare type PriceImpactUnit = 'entryPrice' | 'maxPrice' | 'priceDelta' |
8
8
  * @param direction
9
9
  * @param amount
10
10
  * @param market
11
+ * @param inputAssetType which asset is being traded
12
+ * @param useSpread whether to consider spread with calculating slippage
11
13
  * @return [pctAvgSlippage, pctMaxSlippage, entryPrice, newPrice]
12
14
  *
13
15
  * 'pctAvgSlippage' => the percentage change to entryPrice (average est slippage in execution) : Precision MARK_PRICE_PRECISION
@@ -18,7 +20,7 @@ export declare type PriceImpactUnit = 'entryPrice' | 'maxPrice' | 'priceDelta' |
18
20
  *
19
21
  * 'newPrice' => the price of the asset after the trade : Precision MARK_PRICE_PRECISION
20
22
  */
21
- export declare function calculateTradeSlippage(direction: PositionDirection, amount: BN, market: Market, inputAssetType?: AssetType): [BN, BN, BN, BN];
23
+ export declare function calculateTradeSlippage(direction: PositionDirection, amount: BN, market: Market, inputAssetType?: AssetType, useSpread?: boolean): [BN, BN, BN, BN];
22
24
  /**
23
25
  * Calculates acquired amounts for trade executed
24
26
  * @param direction
@@ -27,7 +29,7 @@ export declare function calculateTradeSlippage(direction: PositionDirection, amo
27
29
  * @param inputAssetType
28
30
  * @param useSpread
29
31
  * @return
30
- * | 'acquiredBase' => positive/negative change in user's base : BN TODO-PRECISION
32
+ * | 'acquiredBase' => positive/negative change in user's base : BN AMM_RESERVE_PRECISION
31
33
  * | 'acquiredQuote' => positive/negative change in user's quote : BN TODO-PRECISION
32
34
  */
33
35
  export declare function calculateTradeAcquiredAmounts(direction: PositionDirection, amount: BN, market: Market, inputAssetType?: AssetType, useSpread?: boolean): [BN, BN];
@@ -37,13 +39,15 @@ export declare function calculateTradeAcquiredAmounts(direction: PositionDirecti
37
39
  * @param market
38
40
  * @param targetPrice
39
41
  * @param pct optional default is 100% gap filling, can set smaller.
42
+ * @param outputAssetType which asset to trade.
43
+ * @param useSpread whether or not to consider the spread when calculating the trade size
40
44
  * @returns trade direction/size in order to push price to a targetPrice,
41
45
  *
42
46
  * [
43
- * direction => direction of trade required, TODO-PRECISION
47
+ * direction => direction of trade required, PositionDirection
44
48
  * tradeSize => size of trade required, TODO-PRECISION
45
- * entryPrice => the entry price for the trade, TODO-PRECISION
46
- * targetPrice => the target price TODO-PRECISION
49
+ * entryPrice => the entry price for the trade, MARK_PRICE_PRECISION
50
+ * targetPrice => the target price MARK_PRICE_PRECISION
47
51
  * ]
48
52
  */
49
- export declare function calculateTargetPriceTrade(market: Market, targetPrice: BN, pct?: BN, outputAssetType?: AssetType): [PositionDirection, BN, BN, BN];
53
+ export declare function calculateTargetPriceTrade(market: Market, targetPrice: BN, pct?: BN, outputAssetType?: AssetType, useSpread?: boolean): [PositionDirection, BN, BN, BN];
package/lib/math/trade.js CHANGED
@@ -8,12 +8,15 @@ const numericConstants_1 = require("../constants/numericConstants");
8
8
  const market_1 = require("./market");
9
9
  const amm_1 = require("./amm");
10
10
  const utils_1 = require("./utils");
11
+ const types_2 = require("../types");
11
12
  const MAXPCT = new anchor_1.BN(1000); //percentage units are [0,1000] => [0,1]
12
13
  /**
13
14
  * Calculates avg/max slippage (price impact) for candidate trade
14
15
  * @param direction
15
16
  * @param amount
16
17
  * @param market
18
+ * @param inputAssetType which asset is being traded
19
+ * @param useSpread whether to consider spread with calculating slippage
17
20
  * @return [pctAvgSlippage, pctMaxSlippage, entryPrice, newPrice]
18
21
  *
19
22
  * 'pctAvgSlippage' => the percentage change to entryPrice (average est slippage in execution) : Precision MARK_PRICE_PRECISION
@@ -24,14 +27,38 @@ const MAXPCT = new anchor_1.BN(1000); //percentage units are [0,1000] => [0,1]
24
27
  *
25
28
  * 'newPrice' => the price of the asset after the trade : Precision MARK_PRICE_PRECISION
26
29
  */
27
- function calculateTradeSlippage(direction, amount, market, inputAssetType = 'quote') {
28
- const oldPrice = (0, market_1.calculateMarkPrice)(market);
30
+ function calculateTradeSlippage(direction, amount, market, inputAssetType = 'quote', useSpread = true) {
31
+ let oldPrice;
32
+ if (useSpread && market.amm.baseSpread > 0) {
33
+ if ((0, types_2.isVariant)(direction, 'long')) {
34
+ oldPrice = (0, market_1.calculateAskPrice)(market);
35
+ }
36
+ else {
37
+ oldPrice = (0, market_1.calculateBidPrice)(market);
38
+ }
39
+ }
40
+ else {
41
+ oldPrice = (0, market_1.calculateMarkPrice)(market);
42
+ }
29
43
  if (amount.eq(numericConstants_1.ZERO)) {
30
44
  return [numericConstants_1.ZERO, numericConstants_1.ZERO, oldPrice, oldPrice];
31
45
  }
32
- const [acquiredBase, acquiredQuote] = calculateTradeAcquiredAmounts(direction, amount, market, inputAssetType);
46
+ const [acquiredBase, acquiredQuote] = calculateTradeAcquiredAmounts(direction, amount, market, inputAssetType, useSpread);
33
47
  const entryPrice = (0, amm_1.calculatePrice)(acquiredBase, acquiredQuote, market.amm.pegMultiplier).mul(new anchor_1.BN(-1));
34
- const newPrice = (0, amm_1.calculatePrice)(market.amm.baseAssetReserve.sub(acquiredBase), market.amm.quoteAssetReserve.sub(acquiredQuote), market.amm.pegMultiplier);
48
+ let amm;
49
+ if (useSpread && market.amm.baseSpread > 0) {
50
+ const { baseAssetReserve, quoteAssetReserve } = (0, amm_1.calculateSpreadReserves)(market.amm, direction);
51
+ amm = {
52
+ baseAssetReserve,
53
+ quoteAssetReserve,
54
+ sqrtK: market.amm.sqrtK,
55
+ pegMultiplier: market.amm.pegMultiplier,
56
+ };
57
+ }
58
+ else {
59
+ amm = market.amm;
60
+ }
61
+ const newPrice = (0, amm_1.calculatePrice)(amm.baseAssetReserve.sub(acquiredBase), amm.quoteAssetReserve.sub(acquiredQuote), amm.pegMultiplier);
35
62
  if (direction == types_1.PositionDirection.SHORT) {
36
63
  (0, assert_1.assert)(newPrice.lt(oldPrice));
37
64
  }
@@ -59,7 +86,7 @@ exports.calculateTradeSlippage = calculateTradeSlippage;
59
86
  * @param inputAssetType
60
87
  * @param useSpread
61
88
  * @return
62
- * | 'acquiredBase' => positive/negative change in user's base : BN TODO-PRECISION
89
+ * | 'acquiredBase' => positive/negative change in user's base : BN AMM_RESERVE_PRECISION
63
90
  * | 'acquiredQuote' => positive/negative change in user's quote : BN TODO-PRECISION
64
91
  */
65
92
  function calculateTradeAcquiredAmounts(direction, amount, market, inputAssetType = 'quote', useSpread = true) {
@@ -92,35 +119,50 @@ exports.calculateTradeAcquiredAmounts = calculateTradeAcquiredAmounts;
92
119
  * @param market
93
120
  * @param targetPrice
94
121
  * @param pct optional default is 100% gap filling, can set smaller.
122
+ * @param outputAssetType which asset to trade.
123
+ * @param useSpread whether or not to consider the spread when calculating the trade size
95
124
  * @returns trade direction/size in order to push price to a targetPrice,
96
125
  *
97
126
  * [
98
- * direction => direction of trade required, TODO-PRECISION
127
+ * direction => direction of trade required, PositionDirection
99
128
  * tradeSize => size of trade required, TODO-PRECISION
100
- * entryPrice => the entry price for the trade, TODO-PRECISION
101
- * targetPrice => the target price TODO-PRECISION
129
+ * entryPrice => the entry price for the trade, MARK_PRICE_PRECISION
130
+ * targetPrice => the target price MARK_PRICE_PRECISION
102
131
  * ]
103
132
  */
104
- function calculateTargetPriceTrade(market, targetPrice, pct = MAXPCT, outputAssetType = 'quote') {
133
+ function calculateTargetPriceTrade(market, targetPrice, pct = MAXPCT, outputAssetType = 'quote', useSpread = true) {
105
134
  (0, assert_1.assert)(market.amm.baseAssetReserve.gt(numericConstants_1.ZERO));
106
135
  (0, assert_1.assert)(targetPrice.gt(numericConstants_1.ZERO));
107
136
  (0, assert_1.assert)(pct.lte(MAXPCT) && pct.gt(numericConstants_1.ZERO));
108
137
  const markPriceBefore = (0, market_1.calculateMarkPrice)(market);
138
+ const bidPriceBefore = (0, market_1.calculateBidPrice)(market);
139
+ const askPriceBefore = (0, market_1.calculateAskPrice)(market);
140
+ let direction;
109
141
  if (targetPrice.gt(markPriceBefore)) {
110
142
  const priceGap = targetPrice.sub(markPriceBefore);
111
143
  const priceGapScaled = priceGap.mul(pct).div(MAXPCT);
112
144
  targetPrice = markPriceBefore.add(priceGapScaled);
145
+ direction = types_1.PositionDirection.LONG;
113
146
  }
114
147
  else {
115
148
  const priceGap = markPriceBefore.sub(targetPrice);
116
149
  const priceGapScaled = priceGap.mul(pct).div(MAXPCT);
117
150
  targetPrice = markPriceBefore.sub(priceGapScaled);
151
+ direction = types_1.PositionDirection.SHORT;
118
152
  }
119
- let direction;
120
153
  let tradeSize;
121
154
  let baseSize;
122
- const baseAssetReserveBefore = market.amm.baseAssetReserve;
123
- const quoteAssetReserveBefore = market.amm.quoteAssetReserve;
155
+ let baseAssetReserveBefore;
156
+ let quoteAssetReserveBefore;
157
+ if (useSpread && market.amm.baseSpread > 0) {
158
+ const { baseAssetReserve, quoteAssetReserve } = (0, amm_1.calculateSpreadReserves)(market.amm, direction);
159
+ baseAssetReserveBefore = baseAssetReserve;
160
+ quoteAssetReserveBefore = quoteAssetReserve;
161
+ }
162
+ else {
163
+ baseAssetReserveBefore = market.amm.baseAssetReserve;
164
+ quoteAssetReserveBefore = market.amm.quoteAssetReserve;
165
+ }
124
166
  const peg = market.amm.pegMultiplier;
125
167
  const invariant = market.amm.sqrtK.mul(market.amm.sqrtK);
126
168
  const k = invariant.mul(numericConstants_1.MARK_PRICE_PRECISION);
@@ -128,7 +170,20 @@ function calculateTargetPriceTrade(market, targetPrice, pct = MAXPCT, outputAsse
128
170
  let quoteAssetReserveAfter;
129
171
  const biasModifier = new anchor_1.BN(1);
130
172
  let markPriceAfter;
131
- if (markPriceBefore.gt(targetPrice)) {
173
+ if (useSpread &&
174
+ targetPrice.lt(askPriceBefore) &&
175
+ targetPrice.gt(bidPriceBefore)) {
176
+ // no trade, market is at target
177
+ if (markPriceBefore.gt(targetPrice)) {
178
+ direction = types_1.PositionDirection.SHORT;
179
+ }
180
+ else {
181
+ direction = types_1.PositionDirection.LONG;
182
+ }
183
+ tradeSize = numericConstants_1.ZERO;
184
+ return [direction, tradeSize, targetPrice, targetPrice];
185
+ }
186
+ else if (markPriceBefore.gt(targetPrice)) {
132
187
  // overestimate y2
133
188
  baseAssetReserveAfter = (0, utils_1.squareRootBN)(k.div(targetPrice).mul(peg).div(numericConstants_1.PEG_PRECISION).sub(biasModifier)).sub(new anchor_1.BN(1));
134
189
  quoteAssetReserveAfter = k
@@ -38,6 +38,7 @@ class PythClient {
38
38
  confidence: convertPythPrice(priceData.confidence, priceData.exponent),
39
39
  twap: convertPythPrice(priceData.twap.value, priceData.exponent),
40
40
  twapConfidence: convertPythPrice(priceData.twac.value, priceData.exponent),
41
+ hasSufficientNumberOfDataPoints: true,
41
42
  };
42
43
  });
43
44
  }
@@ -39,11 +39,14 @@ class SwitchboardClient {
39
39
  const price = convertSwitchboardDecimal(aggregatorAccountData.latestConfirmedRound.result);
40
40
  const confidence = convertSwitchboardDecimal(aggregatorAccountData.latestConfirmedRound
41
41
  .stdDeviation);
42
+ const hasSufficientNumberOfDataPoints = aggregatorAccountData.latestConfirmedRound.numSuccess >=
43
+ aggregatorAccountData.minOracleResults;
42
44
  const slot = aggregatorAccountData.latestConfirmedRound.roundOpenSlot;
43
45
  return {
44
46
  price,
45
47
  slot,
46
48
  confidence,
49
+ hasSufficientNumberOfDataPoints,
47
50
  };
48
51
  });
49
52
  }
@@ -6,6 +6,7 @@ export declare type OraclePriceData = {
6
6
  price: BN;
7
7
  slot: BN;
8
8
  confidence: BN;
9
+ hasSufficientNumberOfDataPoints: boolean;
9
10
  twap?: BN;
10
11
  twapConfidence?: BN;
11
12
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "0.1.36-master.5",
3
+ "version": "0.1.36-master.6",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "author": "crispheaney",
@@ -24,6 +24,7 @@ export class BulkAccountLoader {
24
24
  loadPromise?: Promise<void>;
25
25
  loadPromiseResolver: () => void;
26
26
  lastTimeLoadingPromiseCleared = Date.now();
27
+ mostRecentSlot = 0;
27
28
 
28
29
  public constructor(
29
30
  connection: Connection,
@@ -159,6 +160,10 @@ export class BulkAccountLoader {
159
160
 
160
161
  const newSlot = rpcResponse.result.context.slot;
161
162
 
163
+ if (newSlot > this.mostRecentSlot) {
164
+ this.mostRecentSlot = newSlot;
165
+ }
166
+
162
167
  for (const i in accountsToLoad) {
163
168
  const accountToLoad = accountsToLoad[i];
164
169
  const key = accountToLoad.publicKey.toString();
package/src/index.ts CHANGED
@@ -26,6 +26,7 @@ export * from './math/funding';
26
26
  export * from './math/insuranceFund';
27
27
  export * from './math/market';
28
28
  export * from './math/position';
29
+ export * from './math/oracles';
29
30
  export * from './math/amm';
30
31
  export * from './math/trade';
31
32
  export * from './math/orders';
@@ -0,0 +1,36 @@
1
+ import { AMM, OracleGuardRails } from '../types';
2
+ import { OraclePriceData } from '../oracles/types';
3
+ import { ONE, ZERO } from '../constants/numericConstants';
4
+ import { BN } from '../index';
5
+
6
+ export function isOracleValid(
7
+ amm: AMM,
8
+ oraclePriceData: OraclePriceData,
9
+ oracleGuardRails: OracleGuardRails,
10
+ slot: number
11
+ ): boolean {
12
+ const isOraclePriceNonPositive = oraclePriceData.price.lt(ZERO);
13
+ const isOraclePriceTooVolatile =
14
+ oraclePriceData.price
15
+ .div(BN.max(ONE, amm.lastOraclePriceTwap))
16
+ .gt(oracleGuardRails.validity.tooVolatileRatio) ||
17
+ amm.lastOraclePriceTwap
18
+ .div(BN.max(ONE, oraclePriceData.price))
19
+ .gt(oracleGuardRails.validity.tooVolatileRatio);
20
+
21
+ const isConfidenceTooLarge = oraclePriceData.price
22
+ .div(BN.max(ONE, oraclePriceData.confidence))
23
+ .lt(oracleGuardRails.validity.confidenceIntervalMaxSize);
24
+
25
+ const oracleIsStale = oraclePriceData.slot
26
+ .sub(new BN(slot))
27
+ .gt(oracleGuardRails.validity.slotsBeforeStale);
28
+
29
+ return !(
30
+ !oraclePriceData.hasSufficientNumberOfDataPoints ||
31
+ oracleIsStale ||
32
+ isOraclePriceNonPositive ||
33
+ isOraclePriceTooVolatile ||
34
+ isConfidenceTooLarge
35
+ );
36
+ }
package/src/math/trade.ts CHANGED
@@ -7,7 +7,11 @@ import {
7
7
  AMM_TO_QUOTE_PRECISION_RATIO,
8
8
  ZERO,
9
9
  } from '../constants/numericConstants';
10
- import { calculateMarkPrice } from './market';
10
+ import {
11
+ calculateBidPrice,
12
+ calculateAskPrice,
13
+ calculateMarkPrice,
14
+ } from './market';
11
15
  import {
12
16
  calculateAmmReservesAfterSwap,
13
17
  calculatePrice,
@@ -16,6 +20,7 @@ import {
16
20
  calculateSpreadReserves,
17
21
  } from './amm';
18
22
  import { squareRootBN } from './utils';
23
+ import { isVariant } from '../types';
19
24
 
20
25
  const MAXPCT = new BN(1000); //percentage units are [0,1000] => [0,1]
21
26
 
@@ -37,6 +42,8 @@ export type PriceImpactUnit =
37
42
  * @param direction
38
43
  * @param amount
39
44
  * @param market
45
+ * @param inputAssetType which asset is being traded
46
+ * @param useSpread whether to consider spread with calculating slippage
40
47
  * @return [pctAvgSlippage, pctMaxSlippage, entryPrice, newPrice]
41
48
  *
42
49
  * 'pctAvgSlippage' => the percentage change to entryPrice (average est slippage in execution) : Precision MARK_PRICE_PRECISION
@@ -51,9 +58,20 @@ export function calculateTradeSlippage(
51
58
  direction: PositionDirection,
52
59
  amount: BN,
53
60
  market: Market,
54
- inputAssetType: AssetType = 'quote'
61
+ inputAssetType: AssetType = 'quote',
62
+ useSpread = true
55
63
  ): [BN, BN, BN, BN] {
56
- const oldPrice = calculateMarkPrice(market);
64
+ let oldPrice: BN;
65
+
66
+ if (useSpread && market.amm.baseSpread > 0) {
67
+ if (isVariant(direction, 'long')) {
68
+ oldPrice = calculateAskPrice(market);
69
+ } else {
70
+ oldPrice = calculateBidPrice(market);
71
+ }
72
+ } else {
73
+ oldPrice = calculateMarkPrice(market);
74
+ }
57
75
  if (amount.eq(ZERO)) {
58
76
  return [ZERO, ZERO, oldPrice, oldPrice];
59
77
  }
@@ -61,7 +79,8 @@ export function calculateTradeSlippage(
61
79
  direction,
62
80
  amount,
63
81
  market,
64
- inputAssetType
82
+ inputAssetType,
83
+ useSpread
65
84
  );
66
85
 
67
86
  const entryPrice = calculatePrice(
@@ -70,10 +89,26 @@ export function calculateTradeSlippage(
70
89
  market.amm.pegMultiplier
71
90
  ).mul(new BN(-1));
72
91
 
92
+ let amm: Parameters<typeof calculateAmmReservesAfterSwap>[0];
93
+ if (useSpread && market.amm.baseSpread > 0) {
94
+ const { baseAssetReserve, quoteAssetReserve } = calculateSpreadReserves(
95
+ market.amm,
96
+ direction
97
+ );
98
+ amm = {
99
+ baseAssetReserve,
100
+ quoteAssetReserve,
101
+ sqrtK: market.amm.sqrtK,
102
+ pegMultiplier: market.amm.pegMultiplier,
103
+ };
104
+ } else {
105
+ amm = market.amm;
106
+ }
107
+
73
108
  const newPrice = calculatePrice(
74
- market.amm.baseAssetReserve.sub(acquiredBase),
75
- market.amm.quoteAssetReserve.sub(acquiredQuote),
76
- market.amm.pegMultiplier
109
+ amm.baseAssetReserve.sub(acquiredBase),
110
+ amm.quoteAssetReserve.sub(acquiredQuote),
111
+ amm.pegMultiplier
77
112
  );
78
113
 
79
114
  if (direction == PositionDirection.SHORT) {
@@ -104,7 +139,7 @@ export function calculateTradeSlippage(
104
139
  * @param inputAssetType
105
140
  * @param useSpread
106
141
  * @return
107
- * | 'acquiredBase' => positive/negative change in user's base : BN TODO-PRECISION
142
+ * | 'acquiredBase' => positive/negative change in user's base : BN AMM_RESERVE_PRECISION
108
143
  * | 'acquiredQuote' => positive/negative change in user's quote : BN TODO-PRECISION
109
144
  */
110
145
  export function calculateTradeAcquiredAmounts(
@@ -150,43 +185,63 @@ export function calculateTradeAcquiredAmounts(
150
185
  * @param market
151
186
  * @param targetPrice
152
187
  * @param pct optional default is 100% gap filling, can set smaller.
188
+ * @param outputAssetType which asset to trade.
189
+ * @param useSpread whether or not to consider the spread when calculating the trade size
153
190
  * @returns trade direction/size in order to push price to a targetPrice,
154
191
  *
155
192
  * [
156
- * direction => direction of trade required, TODO-PRECISION
193
+ * direction => direction of trade required, PositionDirection
157
194
  * tradeSize => size of trade required, TODO-PRECISION
158
- * entryPrice => the entry price for the trade, TODO-PRECISION
159
- * targetPrice => the target price TODO-PRECISION
195
+ * entryPrice => the entry price for the trade, MARK_PRICE_PRECISION
196
+ * targetPrice => the target price MARK_PRICE_PRECISION
160
197
  * ]
161
198
  */
162
199
  export function calculateTargetPriceTrade(
163
200
  market: Market,
164
201
  targetPrice: BN,
165
202
  pct: BN = MAXPCT,
166
- outputAssetType: AssetType = 'quote'
203
+ outputAssetType: AssetType = 'quote',
204
+ useSpread = true
167
205
  ): [PositionDirection, BN, BN, BN] {
168
206
  assert(market.amm.baseAssetReserve.gt(ZERO));
169
207
  assert(targetPrice.gt(ZERO));
170
208
  assert(pct.lte(MAXPCT) && pct.gt(ZERO));
171
209
 
172
210
  const markPriceBefore = calculateMarkPrice(market);
211
+ const bidPriceBefore = calculateBidPrice(market);
212
+ const askPriceBefore = calculateAskPrice(market);
173
213
 
214
+ let direction;
174
215
  if (targetPrice.gt(markPriceBefore)) {
175
216
  const priceGap = targetPrice.sub(markPriceBefore);
176
217
  const priceGapScaled = priceGap.mul(pct).div(MAXPCT);
177
218
  targetPrice = markPriceBefore.add(priceGapScaled);
219
+ direction = PositionDirection.LONG;
178
220
  } else {
179
221
  const priceGap = markPriceBefore.sub(targetPrice);
180
222
  const priceGapScaled = priceGap.mul(pct).div(MAXPCT);
181
223
  targetPrice = markPriceBefore.sub(priceGapScaled);
224
+ direction = PositionDirection.SHORT;
182
225
  }
183
226
 
184
- let direction;
185
227
  let tradeSize;
186
228
  let baseSize;
187
229
 
188
- const baseAssetReserveBefore = market.amm.baseAssetReserve;
189
- const quoteAssetReserveBefore = market.amm.quoteAssetReserve;
230
+ let baseAssetReserveBefore: BN;
231
+ let quoteAssetReserveBefore: BN;
232
+
233
+ if (useSpread && market.amm.baseSpread > 0) {
234
+ const { baseAssetReserve, quoteAssetReserve } = calculateSpreadReserves(
235
+ market.amm,
236
+ direction
237
+ );
238
+ baseAssetReserveBefore = baseAssetReserve;
239
+ quoteAssetReserveBefore = quoteAssetReserve;
240
+ } else {
241
+ baseAssetReserveBefore = market.amm.baseAssetReserve;
242
+ quoteAssetReserveBefore = market.amm.quoteAssetReserve;
243
+ }
244
+
190
245
  const peg = market.amm.pegMultiplier;
191
246
  const invariant = market.amm.sqrtK.mul(market.amm.sqrtK);
192
247
  const k = invariant.mul(MARK_PRICE_PRECISION);
@@ -196,7 +251,20 @@ export function calculateTargetPriceTrade(
196
251
  const biasModifier = new BN(1);
197
252
  let markPriceAfter;
198
253
 
199
- if (markPriceBefore.gt(targetPrice)) {
254
+ if (
255
+ useSpread &&
256
+ targetPrice.lt(askPriceBefore) &&
257
+ targetPrice.gt(bidPriceBefore)
258
+ ) {
259
+ // no trade, market is at target
260
+ if (markPriceBefore.gt(targetPrice)) {
261
+ direction = PositionDirection.SHORT;
262
+ } else {
263
+ direction = PositionDirection.LONG;
264
+ }
265
+ tradeSize = ZERO;
266
+ return [direction, tradeSize, targetPrice, targetPrice];
267
+ } else if (markPriceBefore.gt(targetPrice)) {
200
268
  // overestimate y2
201
269
  baseAssetReserveAfter = squareRootBN(
202
270
  k.div(targetPrice).mul(peg).div(PEG_PRECISION).sub(biasModifier)
@@ -36,6 +36,7 @@ export class PythClient {
36
36
  priceData.twac.value,
37
37
  priceData.exponent
38
38
  ),
39
+ hasSufficientNumberOfDataPoints: true,
39
40
  };
40
41
  }
41
42
  }
@@ -48,11 +48,16 @@ export class SwitchboardClient implements OracleClient {
48
48
  .stdDeviation as SwitchboardDecimal
49
49
  );
50
50
 
51
+ const hasSufficientNumberOfDataPoints =
52
+ aggregatorAccountData.latestConfirmedRound.numSuccess >=
53
+ aggregatorAccountData.minOracleResults;
54
+
51
55
  const slot: BN = aggregatorAccountData.latestConfirmedRound.roundOpenSlot;
52
56
  return {
53
57
  price,
54
58
  slot,
55
59
  confidence,
60
+ hasSufficientNumberOfDataPoints,
56
61
  };
57
62
  }
58
63
 
@@ -5,6 +5,7 @@ export type OraclePriceData = {
5
5
  price: BN;
6
6
  slot: BN;
7
7
  confidence: BN;
8
+ hasSufficientNumberOfDataPoints: boolean;
8
9
  twap?: BN;
9
10
  twapConfidence?: BN;
10
11
  };