@drift-labs/sdk 0.2.0-master.3 → 0.2.0-master.30
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/README.md +13 -13
- package/lib/accounts/bulkUserStatsSubscription.d.ts +7 -0
- package/lib/accounts/bulkUserStatsSubscription.js +21 -0
- package/lib/accounts/bulkUserSubscription.js +0 -1
- package/lib/accounts/fetch.d.ts +2 -1
- package/lib/accounts/fetch.js +9 -1
- package/lib/accounts/pollingClearingHouseAccountSubscriber.d.ts +16 -16
- package/lib/accounts/pollingClearingHouseAccountSubscriber.js +31 -28
- package/lib/accounts/pollingUserStatsAccountSubscriber.d.ts +27 -0
- package/lib/accounts/pollingUserStatsAccountSubscriber.js +113 -0
- package/lib/accounts/types.d.ts +22 -11
- package/lib/accounts/webSocketClearingHouseAccountSubscriber.d.ts +17 -17
- package/lib/accounts/webSocketClearingHouseAccountSubscriber.js +37 -35
- package/lib/accounts/webSocketUserStatsAccountSubsriber.d.ts +20 -0
- package/lib/accounts/webSocketUserStatsAccountSubsriber.js +47 -0
- package/lib/addresses/marketAddresses.d.ts +1 -3
- package/lib/addresses/pda.d.ts +10 -4
- package/lib/addresses/pda.js +51 -14
- package/lib/admin.d.ts +34 -22
- package/lib/admin.js +182 -73
- package/lib/clearingHouse.d.ts +120 -42
- package/lib/clearingHouse.js +1505 -254
- package/lib/clearingHouseConfig.d.ts +4 -4
- package/lib/clearingHouseUser.d.ts +50 -38
- package/lib/clearingHouseUser.js +410 -190
- package/lib/clearingHouseUserStats.d.ts +18 -0
- package/lib/clearingHouseUserStats.js +49 -0
- package/lib/clearingHouseUserStatsConfig.d.ts +14 -0
- package/lib/clearingHouseUserStatsConfig.js +2 -0
- package/lib/config.d.ts +7 -9
- package/lib/config.js +21 -21
- package/lib/constants/numericConstants.d.ts +18 -12
- package/lib/constants/numericConstants.js +28 -21
- package/lib/constants/perpMarkets.d.ts +18 -0
- package/lib/constants/{markets.js → perpMarkets.js} +7 -7
- package/lib/constants/spotMarkets.d.ts +19 -0
- package/lib/constants/spotMarkets.js +53 -0
- package/lib/dlob/DLOB.d.ts +73 -0
- package/lib/dlob/DLOB.js +553 -0
- package/lib/dlob/DLOBNode.d.ts +52 -0
- package/lib/dlob/DLOBNode.js +82 -0
- package/lib/dlob/NodeList.d.ts +26 -0
- package/lib/dlob/NodeList.js +138 -0
- package/lib/events/eventList.js +3 -0
- package/lib/events/eventSubscriber.d.ts +4 -2
- package/lib/events/eventSubscriber.js +16 -9
- package/lib/events/fetchLogs.d.ts +10 -1
- package/lib/events/fetchLogs.js +27 -7
- package/lib/events/pollingLogProvider.d.ts +2 -1
- package/lib/events/pollingLogProvider.js +6 -2
- package/lib/events/sort.js +8 -11
- package/lib/events/types.d.ts +7 -2
- package/lib/events/types.js +5 -0
- package/lib/examples/makeTradeExample.js +20 -8
- package/lib/factory/bigNum.d.ts +1 -0
- package/lib/factory/bigNum.js +34 -10
- package/lib/idl/clearing_house.json +4313 -1433
- package/lib/idl/{mock_usdc_faucet.json → token_faucet.json} +46 -23
- package/lib/index.d.ts +21 -6
- package/lib/index.js +25 -6
- package/lib/math/amm.d.ts +8 -5
- package/lib/math/amm.js +68 -46
- package/lib/math/auction.js +4 -1
- package/lib/math/conversion.js +1 -1
- package/lib/math/funding.d.ts +6 -6
- package/lib/math/funding.js +11 -10
- package/lib/math/insurance.d.ts +4 -0
- package/lib/math/insurance.js +27 -0
- package/lib/math/margin.d.ts +11 -0
- package/lib/math/margin.js +77 -0
- package/lib/math/market.d.ts +14 -9
- package/lib/math/market.js +69 -7
- package/lib/math/oracles.d.ts +4 -0
- package/lib/math/oracles.js +36 -8
- package/lib/math/orders.d.ts +6 -2
- package/lib/math/orders.js +78 -3
- package/lib/math/position.d.ts +21 -13
- package/lib/math/position.js +76 -36
- package/lib/math/repeg.js +14 -5
- package/lib/math/spotBalance.d.ts +22 -0
- package/lib/math/spotBalance.js +193 -0
- package/lib/math/spotMarket.d.ts +4 -0
- package/lib/math/spotMarket.js +8 -0
- package/lib/math/spotPosition.d.ts +6 -0
- package/lib/math/spotPosition.js +23 -0
- package/lib/math/trade.d.ts +10 -10
- package/lib/math/trade.js +22 -29
- package/lib/oracles/pythClient.js +1 -1
- package/lib/oracles/quoteAssetOracleClient.js +1 -1
- package/lib/oracles/switchboardClient.js +1 -1
- package/lib/orderParams.d.ts +14 -5
- package/lib/orderParams.js +12 -92
- package/lib/serum/serumSubscriber.d.ts +23 -0
- package/lib/serum/serumSubscriber.js +41 -0
- package/lib/serum/types.d.ts +11 -0
- package/lib/serum/types.js +2 -0
- package/lib/slot/SlotSubscriber.d.ts +7 -0
- package/lib/slot/SlotSubscriber.js +3 -0
- package/lib/{mockUSDCFaucet.d.ts → tokenFaucet.d.ts} +8 -5
- package/lib/{mockUSDCFaucet.js → tokenFaucet.js} +63 -51
- package/lib/tx/retryTxSender.d.ts +1 -1
- package/lib/tx/retryTxSender.js +13 -4
- package/lib/tx/types.d.ts +1 -1
- package/lib/tx/utils.js +1 -1
- package/lib/types.d.ts +474 -123
- package/lib/types.js +99 -5
- package/lib/userMap/userMap.d.ts +25 -0
- package/lib/userMap/userMap.js +73 -0
- package/lib/userMap/userStatsMap.d.ts +19 -0
- package/lib/userMap/userStatsMap.js +68 -0
- package/lib/util/computeUnits.js +1 -1
- package/lib/util/getTokenAddress.d.ts +2 -0
- package/lib/util/getTokenAddress.js +9 -0
- package/package.json +9 -5
- package/src/accounts/bulkUserStatsSubscription.ts +33 -0
- package/src/accounts/bulkUserSubscription.ts +0 -1
- package/src/accounts/fetch.ts +27 -2
- package/src/accounts/pollingClearingHouseAccountSubscriber.ts +46 -42
- package/src/accounts/pollingUserStatsAccountSubscriber.ts +172 -0
- package/src/accounts/types.ts +31 -11
- package/src/accounts/webSocketClearingHouseAccountSubscriber.ts +64 -59
- package/src/accounts/webSocketUserStatsAccountSubsriber.ts +80 -0
- package/src/addresses/marketAddresses.ts +1 -2
- package/src/addresses/pda.ts +88 -14
- package/src/admin.ts +333 -128
- package/src/assert/assert.js +9 -0
- package/src/clearingHouse.ts +2464 -458
- package/src/clearingHouseConfig.ts +4 -3
- package/src/clearingHouseUser.ts +747 -291
- package/src/clearingHouseUserStats.ts +75 -0
- package/src/clearingHouseUserStatsConfig.ts +18 -0
- package/src/config.ts +30 -31
- package/src/constants/numericConstants.ts +41 -25
- package/src/constants/{markets.ts → perpMarkets.ts} +10 -10
- package/src/constants/spotMarkets.ts +72 -0
- package/src/dlob/DLOB.ts +868 -0
- package/src/dlob/DLOBNode.ts +162 -0
- package/src/dlob/NodeList.ts +185 -0
- package/src/events/eventList.js +77 -0
- package/src/events/eventList.ts +3 -0
- package/src/events/eventSubscriber.ts +20 -12
- package/src/events/fetchLogs.ts +35 -8
- package/src/events/pollingLogProvider.ts +10 -2
- package/src/events/sort.ts +11 -15
- package/src/events/types.ts +16 -1
- package/src/examples/makeTradeExample.js +157 -0
- package/src/examples/makeTradeExample.ts +32 -14
- package/src/factory/bigNum.ts +42 -13
- package/src/idl/clearing_house.json +4313 -1433
- package/src/idl/{mock_usdc_faucet.json → token_faucet.json} +46 -23
- package/src/index.ts +21 -6
- package/src/math/amm.ts +136 -66
- package/src/math/auction.ts +5 -1
- package/src/math/conversion.ts +2 -2
- package/src/math/funding.ts +20 -18
- package/src/math/insurance.ts +35 -0
- package/src/math/margin.ts +127 -0
- package/src/math/market.ts +138 -12
- package/src/math/oracles.ts +63 -9
- package/src/math/orders.ts +138 -4
- package/src/math/position.ts +119 -58
- package/src/math/repeg.ts +16 -6
- package/src/math/spotBalance.ts +316 -0
- package/src/math/spotMarket.ts +9 -0
- package/src/math/spotPosition.ts +47 -0
- package/src/math/trade.ts +43 -49
- package/src/oracles/pythClient.ts +2 -2
- package/src/oracles/quoteAssetOracleClient.ts +2 -2
- package/src/oracles/switchboardClient.ts +2 -2
- package/src/orderParams.ts +24 -137
- package/src/serum/serumSubscriber.ts +80 -0
- package/src/serum/types.ts +13 -0
- package/src/slot/SlotSubscriber.ts +11 -1
- package/src/token/index.js +38 -0
- package/src/{mockUSDCFaucet.ts → tokenFaucet.ts} +82 -70
- package/src/tx/retryTxSender.ts +16 -5
- package/src/tx/types.js +2 -0
- package/src/tx/types.ts +2 -1
- package/src/tx/utils.js +17 -0
- package/src/tx/utils.ts +1 -1
- package/src/types.ts +477 -125
- package/src/userMap/userMap.ts +100 -0
- package/src/userMap/userStatsMap.ts +110 -0
- package/src/util/computeUnits.js +21 -11
- package/src/util/computeUnits.ts +1 -1
- package/src/util/getTokenAddress.js +9 -0
- package/src/util/getTokenAddress.ts +18 -0
- package/src/util/promiseTimeout.js +14 -0
- package/src/util/tps.js +27 -0
- package/tests/bn/test.ts +12 -3
- package/tests/dlob/helpers.ts +374 -0
- package/tests/dlob/test.ts +2865 -0
- package/lib/constants/banks.d.ts +0 -16
- package/lib/constants/banks.js +0 -34
- package/lib/constants/markets.d.ts +0 -19
- package/lib/math/bankBalance.d.ts +0 -9
- package/lib/math/bankBalance.js +0 -75
- package/lib/math/state.d.ts +0 -8
- package/lib/math/state.js +0 -15
- package/lib/orders.d.ts +0 -8
- package/lib/orders.js +0 -134
- package/src/constants/banks.ts +0 -43
- package/src/math/bankBalance.ts +0 -112
- package/src/math/state.ts +0 -14
- package/src/math/utils.js +0 -27
- package/src/math/utils.js.map +0 -1
- package/src/orders.ts +0 -244
- package/src/util/computeUnits.js.map +0 -1
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { ZERO } from '../constants/numericConstants';
|
|
2
|
+
import { BN } from '../index';
|
|
3
|
+
|
|
4
|
+
export function stakeAmountToShares(
|
|
5
|
+
amount: BN,
|
|
6
|
+
totalIfShares: BN,
|
|
7
|
+
insuranceFundVaultBalance: BN
|
|
8
|
+
): BN {
|
|
9
|
+
let nShares: BN;
|
|
10
|
+
if (insuranceFundVaultBalance.gt(ZERO)) {
|
|
11
|
+
nShares = amount.mul(totalIfShares).div(insuranceFundVaultBalance);
|
|
12
|
+
} else {
|
|
13
|
+
nShares = amount;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return nShares;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function unstakeSharesToAmount(
|
|
20
|
+
nShares: BN,
|
|
21
|
+
totalIfShares: BN,
|
|
22
|
+
insuranceFundVaultBalance: BN
|
|
23
|
+
): BN {
|
|
24
|
+
let amount: BN;
|
|
25
|
+
if (totalIfShares.gt(ZERO)) {
|
|
26
|
+
amount = BN.max(
|
|
27
|
+
ZERO,
|
|
28
|
+
nShares.mul(insuranceFundVaultBalance).div(totalIfShares)
|
|
29
|
+
);
|
|
30
|
+
} else {
|
|
31
|
+
amount = ZERO;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return amount;
|
|
35
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { squareRootBN } from './utils';
|
|
2
|
+
import {
|
|
3
|
+
SPOT_MARKET_WEIGHT_PRECISION,
|
|
4
|
+
SPOT_MARKET_IMF_PRECISION,
|
|
5
|
+
ZERO,
|
|
6
|
+
BID_ASK_SPREAD_PRECISION,
|
|
7
|
+
AMM_RESERVE_PRECISION,
|
|
8
|
+
} from '../constants/numericConstants';
|
|
9
|
+
import { BN } from '@project-serum/anchor';
|
|
10
|
+
import { OraclePriceData } from '../oracles/types';
|
|
11
|
+
import { PerpMarketAccount, PerpPosition } from '..';
|
|
12
|
+
import { assert } from '../assert/assert';
|
|
13
|
+
|
|
14
|
+
export function calculateSizePremiumLiabilityWeight(
|
|
15
|
+
size: BN, // AMM_RESERVE_PRECISION
|
|
16
|
+
imfFactor: BN,
|
|
17
|
+
liabilityWeight: BN,
|
|
18
|
+
precision: BN
|
|
19
|
+
): BN {
|
|
20
|
+
if (imfFactor.eq(ZERO)) {
|
|
21
|
+
return liabilityWeight;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const sizeSqrt = squareRootBN(size.mul(new BN(10)).add(new BN(1))); //1e9 -> 1e10 -> 1e5
|
|
25
|
+
|
|
26
|
+
const denom0 = BN.max(new BN(1), SPOT_MARKET_IMF_PRECISION.div(imfFactor));
|
|
27
|
+
assert(denom0.gt(ZERO));
|
|
28
|
+
const liabilityWeightNumerator = liabilityWeight.sub(
|
|
29
|
+
liabilityWeight.div(
|
|
30
|
+
BN.max(new BN(1), SPOT_MARKET_IMF_PRECISION.div(imfFactor))
|
|
31
|
+
)
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const denom = new BN(100_000).mul(SPOT_MARKET_IMF_PRECISION).div(precision);
|
|
35
|
+
assert(denom.gt(ZERO));
|
|
36
|
+
|
|
37
|
+
const sizePremiumLiabilityWeight = liabilityWeightNumerator.add(
|
|
38
|
+
sizeSqrt // 1e5
|
|
39
|
+
.mul(imfFactor)
|
|
40
|
+
.div(denom) // 1e5
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const maxLiabilityWeight = BN.max(
|
|
44
|
+
liabilityWeight,
|
|
45
|
+
sizePremiumLiabilityWeight
|
|
46
|
+
);
|
|
47
|
+
return maxLiabilityWeight;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function calculateSizeDiscountAssetWeight(
|
|
51
|
+
size: BN, // AMM_RESERVE_PRECISION
|
|
52
|
+
imfFactor: BN,
|
|
53
|
+
assetWeight: BN
|
|
54
|
+
): BN {
|
|
55
|
+
if (imfFactor.eq(ZERO)) {
|
|
56
|
+
return assetWeight;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const sizeSqrt = squareRootBN(size.mul(new BN(10)).add(new BN(1))); //1e9 -> 1e10 -> 1e5
|
|
60
|
+
const imfNumerator = SPOT_MARKET_IMF_PRECISION.add(
|
|
61
|
+
SPOT_MARKET_IMF_PRECISION.div(new BN(10))
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const sizeDiscountAssetWeight = imfNumerator
|
|
65
|
+
.mul(SPOT_MARKET_WEIGHT_PRECISION)
|
|
66
|
+
.div(
|
|
67
|
+
SPOT_MARKET_IMF_PRECISION.add(
|
|
68
|
+
sizeSqrt // 1e5
|
|
69
|
+
.mul(imfFactor)
|
|
70
|
+
.div(new BN(100_000)) // 1e5
|
|
71
|
+
)
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const minAssetWeight = BN.min(assetWeight, sizeDiscountAssetWeight);
|
|
75
|
+
|
|
76
|
+
return minAssetWeight;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function calculateOraclePriceForPerpMargin(
|
|
80
|
+
perpPosition: PerpPosition,
|
|
81
|
+
market: PerpMarketAccount,
|
|
82
|
+
oraclePriceData: OraclePriceData
|
|
83
|
+
): BN {
|
|
84
|
+
const oraclePriceOffset = BN.min(
|
|
85
|
+
new BN(market.amm.maxSpread)
|
|
86
|
+
.mul(oraclePriceData.price)
|
|
87
|
+
.div(BID_ASK_SPREAD_PRECISION),
|
|
88
|
+
oraclePriceData.confidence.add(
|
|
89
|
+
new BN(market.amm.baseSpread)
|
|
90
|
+
.mul(oraclePriceData.price)
|
|
91
|
+
.div(BID_ASK_SPREAD_PRECISION)
|
|
92
|
+
)
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
let marginPrice: BN;
|
|
96
|
+
if (perpPosition.baseAssetAmount.gt(ZERO)) {
|
|
97
|
+
marginPrice = oraclePriceData.price.sub(oraclePriceOffset);
|
|
98
|
+
} else {
|
|
99
|
+
marginPrice = oraclePriceData.price.add(oraclePriceOffset);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return marginPrice;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function calculateBaseAssetValueWithOracle(
|
|
106
|
+
market: PerpMarketAccount,
|
|
107
|
+
perpPosition: PerpPosition,
|
|
108
|
+
oraclePriceData: OraclePriceData
|
|
109
|
+
): BN {
|
|
110
|
+
return perpPosition.baseAssetAmount
|
|
111
|
+
.abs()
|
|
112
|
+
.mul(oraclePriceData.price)
|
|
113
|
+
.div(AMM_RESERVE_PRECISION);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function calculateWorstCaseBaseAssetAmount(
|
|
117
|
+
perpPosition: PerpPosition
|
|
118
|
+
): BN {
|
|
119
|
+
const allBids = perpPosition.baseAssetAmount.add(perpPosition.openBids);
|
|
120
|
+
const allAsks = perpPosition.baseAssetAmount.add(perpPosition.openAsks);
|
|
121
|
+
|
|
122
|
+
if (allBids.abs().gt(allAsks.abs())) {
|
|
123
|
+
return allBids;
|
|
124
|
+
} else {
|
|
125
|
+
return allAsks;
|
|
126
|
+
}
|
|
127
|
+
}
|
package/src/math/market.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { BN } from '@project-serum/anchor';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
PerpMarketAccount,
|
|
4
|
+
PositionDirection,
|
|
5
|
+
MarginCategory,
|
|
6
|
+
SpotMarketAccount,
|
|
7
|
+
SpotBalanceType,
|
|
8
|
+
} from '../types';
|
|
3
9
|
import {
|
|
4
10
|
calculateAmmReservesAfterSwap,
|
|
5
11
|
calculatePrice,
|
|
@@ -7,16 +13,27 @@ import {
|
|
|
7
13
|
getSwapDirection,
|
|
8
14
|
calculateUpdatedAMM,
|
|
9
15
|
} from './amm';
|
|
16
|
+
import {
|
|
17
|
+
calculateSizeDiscountAssetWeight,
|
|
18
|
+
calculateSizePremiumLiabilityWeight,
|
|
19
|
+
} from './margin';
|
|
10
20
|
import { OraclePriceData } from '../oracles/types';
|
|
21
|
+
import {
|
|
22
|
+
BASE_PRECISION,
|
|
23
|
+
MARGIN_PRECISION,
|
|
24
|
+
PRICE_TO_QUOTE_PRECISION,
|
|
25
|
+
ZERO,
|
|
26
|
+
} from '../constants/numericConstants';
|
|
27
|
+
import { getTokenAmount } from './spotBalance';
|
|
11
28
|
|
|
12
29
|
/**
|
|
13
30
|
* Calculates market mark price
|
|
14
31
|
*
|
|
15
32
|
* @param market
|
|
16
|
-
* @return markPrice : Precision
|
|
33
|
+
* @return markPrice : Precision PRICE_PRECISION
|
|
17
34
|
*/
|
|
18
|
-
export function
|
|
19
|
-
market:
|
|
35
|
+
export function calculateReservePrice(
|
|
36
|
+
market: PerpMarketAccount,
|
|
20
37
|
oraclePriceData: OraclePriceData
|
|
21
38
|
): BN {
|
|
22
39
|
const newAmm = calculateUpdatedAMM(market.amm, oraclePriceData);
|
|
@@ -31,10 +48,10 @@ export function calculateMarkPrice(
|
|
|
31
48
|
* Calculates market bid price
|
|
32
49
|
*
|
|
33
50
|
* @param market
|
|
34
|
-
* @return bidPrice : Precision
|
|
51
|
+
* @return bidPrice : Precision PRICE_PRECISION
|
|
35
52
|
*/
|
|
36
53
|
export function calculateBidPrice(
|
|
37
|
-
market:
|
|
54
|
+
market: PerpMarketAccount,
|
|
38
55
|
oraclePriceData: OraclePriceData
|
|
39
56
|
): BN {
|
|
40
57
|
const { baseAssetReserve, quoteAssetReserve, newPeg } =
|
|
@@ -51,10 +68,10 @@ export function calculateBidPrice(
|
|
|
51
68
|
* Calculates market ask price
|
|
52
69
|
*
|
|
53
70
|
* @param market
|
|
54
|
-
* @return
|
|
71
|
+
* @return askPrice : Precision PRICE_PRECISION
|
|
55
72
|
*/
|
|
56
73
|
export function calculateAskPrice(
|
|
57
|
-
market:
|
|
74
|
+
market: PerpMarketAccount,
|
|
58
75
|
oraclePriceData: OraclePriceData
|
|
59
76
|
): BN {
|
|
60
77
|
const { baseAssetReserve, quoteAssetReserve, newPeg } =
|
|
@@ -70,8 +87,8 @@ export function calculateAskPrice(
|
|
|
70
87
|
export function calculateNewMarketAfterTrade(
|
|
71
88
|
baseAssetAmount: BN,
|
|
72
89
|
direction: PositionDirection,
|
|
73
|
-
market:
|
|
74
|
-
):
|
|
90
|
+
market: PerpMarketAccount
|
|
91
|
+
): PerpMarketAccount {
|
|
75
92
|
const [newQuoteAssetReserve, newBaseAssetReserve] =
|
|
76
93
|
calculateAmmReservesAfterSwap(
|
|
77
94
|
market.amm,
|
|
@@ -90,10 +107,10 @@ export function calculateNewMarketAfterTrade(
|
|
|
90
107
|
}
|
|
91
108
|
|
|
92
109
|
export function calculateMarkOracleSpread(
|
|
93
|
-
market:
|
|
110
|
+
market: PerpMarketAccount,
|
|
94
111
|
oraclePriceData: OraclePriceData
|
|
95
112
|
): BN {
|
|
96
|
-
const markPrice =
|
|
113
|
+
const markPrice = calculateReservePrice(market, oraclePriceData);
|
|
97
114
|
return calculateOracleSpread(markPrice, oraclePriceData);
|
|
98
115
|
}
|
|
99
116
|
|
|
@@ -103,3 +120,112 @@ export function calculateOracleSpread(
|
|
|
103
120
|
): BN {
|
|
104
121
|
return price.sub(oraclePriceData.price);
|
|
105
122
|
}
|
|
123
|
+
|
|
124
|
+
export function calculateMarketMarginRatio(
|
|
125
|
+
market: PerpMarketAccount,
|
|
126
|
+
size: BN,
|
|
127
|
+
marginCategory: MarginCategory
|
|
128
|
+
): number {
|
|
129
|
+
let marginRatio;
|
|
130
|
+
switch (marginCategory) {
|
|
131
|
+
case 'Initial':
|
|
132
|
+
marginRatio = calculateSizePremiumLiabilityWeight(
|
|
133
|
+
size,
|
|
134
|
+
market.imfFactor,
|
|
135
|
+
new BN(market.marginRatioInitial),
|
|
136
|
+
MARGIN_PRECISION
|
|
137
|
+
).toNumber();
|
|
138
|
+
break;
|
|
139
|
+
case 'Maintenance':
|
|
140
|
+
marginRatio = market.marginRatioMaintenance;
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return marginRatio;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function calculateUnrealizedAssetWeight(
|
|
148
|
+
market: PerpMarketAccount,
|
|
149
|
+
quoteSpotMarket: SpotMarketAccount,
|
|
150
|
+
unrealizedPnl: BN,
|
|
151
|
+
marginCategory: MarginCategory,
|
|
152
|
+
oraclePriceData: OraclePriceData
|
|
153
|
+
): BN {
|
|
154
|
+
let assetWeight: BN;
|
|
155
|
+
switch (marginCategory) {
|
|
156
|
+
case 'Initial':
|
|
157
|
+
assetWeight = new BN(market.unrealizedInitialAssetWeight);
|
|
158
|
+
|
|
159
|
+
if (market.unrealizedMaxImbalance.gt(ZERO)) {
|
|
160
|
+
const netUnsettledPnl = calculateNetUserPnlImbalance(
|
|
161
|
+
market,
|
|
162
|
+
quoteSpotMarket,
|
|
163
|
+
oraclePriceData
|
|
164
|
+
);
|
|
165
|
+
if (netUnsettledPnl.gt(market.unrealizedMaxImbalance)) {
|
|
166
|
+
assetWeight = assetWeight
|
|
167
|
+
.mul(market.unrealizedMaxImbalance)
|
|
168
|
+
.div(netUnsettledPnl);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
assetWeight = calculateSizeDiscountAssetWeight(
|
|
173
|
+
unrealizedPnl,
|
|
174
|
+
market.unrealizedImfFactor,
|
|
175
|
+
assetWeight
|
|
176
|
+
);
|
|
177
|
+
break;
|
|
178
|
+
case 'Maintenance':
|
|
179
|
+
assetWeight = new BN(market.unrealizedMaintenanceAssetWeight);
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return assetWeight;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function calculateMarketAvailablePNL(
|
|
187
|
+
perpMarket: PerpMarketAccount,
|
|
188
|
+
spotMarket: SpotMarketAccount
|
|
189
|
+
): BN {
|
|
190
|
+
return getTokenAmount(
|
|
191
|
+
perpMarket.pnlPool.balance,
|
|
192
|
+
spotMarket,
|
|
193
|
+
SpotBalanceType.DEPOSIT
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function calculateNetUserPnl(
|
|
198
|
+
perpMarket: PerpMarketAccount,
|
|
199
|
+
oraclePriceData: OraclePriceData
|
|
200
|
+
): BN {
|
|
201
|
+
const netUserPositionValue = perpMarket.amm.netBaseAssetAmount
|
|
202
|
+
.mul(oraclePriceData.price)
|
|
203
|
+
.div(BASE_PRECISION)
|
|
204
|
+
.div(PRICE_TO_QUOTE_PRECISION);
|
|
205
|
+
|
|
206
|
+
const netUserCostBasis = perpMarket.amm.quoteAssetAmountLong
|
|
207
|
+
.add(perpMarket.amm.quoteAssetAmountShort)
|
|
208
|
+
.sub(perpMarket.amm.cumulativeSocialLoss);
|
|
209
|
+
|
|
210
|
+
const netUserPnl = netUserPositionValue.add(netUserCostBasis);
|
|
211
|
+
|
|
212
|
+
return netUserPnl;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export function calculateNetUserPnlImbalance(
|
|
216
|
+
perpMarket: PerpMarketAccount,
|
|
217
|
+
spotMarket: SpotMarketAccount,
|
|
218
|
+
oraclePriceData: OraclePriceData
|
|
219
|
+
): BN {
|
|
220
|
+
const netUserPnl = calculateNetUserPnl(perpMarket, oraclePriceData);
|
|
221
|
+
|
|
222
|
+
const pnlPool = getTokenAmount(
|
|
223
|
+
perpMarket.pnlPool.balance,
|
|
224
|
+
spotMarket,
|
|
225
|
+
SpotBalanceType.DEPOSIT
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
const imbalance = netUserPnl.sub(pnlPool);
|
|
229
|
+
|
|
230
|
+
return imbalance;
|
|
231
|
+
}
|
package/src/math/oracles.ts
CHANGED
|
@@ -1,7 +1,29 @@
|
|
|
1
1
|
import { AMM, OracleGuardRails } from '../types';
|
|
2
2
|
import { OraclePriceData } from '../oracles/types';
|
|
3
|
-
import {
|
|
4
|
-
|
|
3
|
+
import {
|
|
4
|
+
BID_ASK_SPREAD_PRECISION,
|
|
5
|
+
MARGIN_PRECISION,
|
|
6
|
+
PRICE_PRECISION,
|
|
7
|
+
ONE,
|
|
8
|
+
ZERO,
|
|
9
|
+
} from '../constants/numericConstants';
|
|
10
|
+
import { BN, PerpMarketAccount } from '../index';
|
|
11
|
+
import { assert } from '../assert/assert';
|
|
12
|
+
|
|
13
|
+
export function oraclePriceBands(
|
|
14
|
+
market: PerpMarketAccount,
|
|
15
|
+
oraclePriceData: OraclePriceData
|
|
16
|
+
): [BN, BN] {
|
|
17
|
+
const maxPercentDiff =
|
|
18
|
+
market.marginRatioInitial - market.marginRatioMaintenance;
|
|
19
|
+
const offset = oraclePriceData.price
|
|
20
|
+
.mul(new BN(maxPercentDiff))
|
|
21
|
+
.div(MARGIN_PRECISION);
|
|
22
|
+
|
|
23
|
+
assert(offset.gt(ZERO));
|
|
24
|
+
|
|
25
|
+
return [oraclePriceData.price.sub(offset), oraclePriceData.price.add(offset)];
|
|
26
|
+
}
|
|
5
27
|
|
|
6
28
|
export function isOracleValid(
|
|
7
29
|
amm: AMM,
|
|
@@ -9,22 +31,24 @@ export function isOracleValid(
|
|
|
9
31
|
oracleGuardRails: OracleGuardRails,
|
|
10
32
|
slot: number
|
|
11
33
|
): boolean {
|
|
12
|
-
const isOraclePriceNonPositive = oraclePriceData.price.
|
|
34
|
+
const isOraclePriceNonPositive = oraclePriceData.price.lte(ZERO);
|
|
13
35
|
const isOraclePriceTooVolatile =
|
|
14
36
|
oraclePriceData.price
|
|
15
|
-
.div(BN.max(ONE, amm.lastOraclePriceTwap))
|
|
37
|
+
.div(BN.max(ONE, amm.historicalOracleData.lastOraclePriceTwap))
|
|
16
38
|
.gt(oracleGuardRails.validity.tooVolatileRatio) ||
|
|
17
|
-
amm.lastOraclePriceTwap
|
|
39
|
+
amm.historicalOracleData.lastOraclePriceTwap
|
|
18
40
|
.div(BN.max(ONE, oraclePriceData.price))
|
|
19
41
|
.gt(oracleGuardRails.validity.tooVolatileRatio);
|
|
20
42
|
|
|
21
|
-
const isConfidenceTooLarge =
|
|
22
|
-
.
|
|
23
|
-
.
|
|
43
|
+
const isConfidenceTooLarge = new BN(amm.baseSpread)
|
|
44
|
+
.add(BN.max(ONE, oraclePriceData.confidence))
|
|
45
|
+
.mul(BID_ASK_SPREAD_PRECISION)
|
|
46
|
+
.div(oraclePriceData.price)
|
|
47
|
+
.gt(new BN(amm.maxSpread));
|
|
24
48
|
|
|
25
49
|
const oracleIsStale = oraclePriceData.slot
|
|
26
50
|
.sub(new BN(slot))
|
|
27
|
-
.gt(oracleGuardRails.validity.
|
|
51
|
+
.gt(oracleGuardRails.validity.slotsBeforeStaleForAmm);
|
|
28
52
|
|
|
29
53
|
return !(
|
|
30
54
|
!oraclePriceData.hasSufficientNumberOfDataPoints ||
|
|
@@ -34,3 +58,33 @@ export function isOracleValid(
|
|
|
34
58
|
isConfidenceTooLarge
|
|
35
59
|
);
|
|
36
60
|
}
|
|
61
|
+
|
|
62
|
+
export function isOracleTooDivergent(
|
|
63
|
+
amm: AMM,
|
|
64
|
+
oraclePriceData: OraclePriceData,
|
|
65
|
+
oracleGuardRails: OracleGuardRails,
|
|
66
|
+
now: BN
|
|
67
|
+
): boolean {
|
|
68
|
+
const sinceLastUpdate = now.sub(
|
|
69
|
+
amm.historicalOracleData.lastOraclePriceTwapTs
|
|
70
|
+
);
|
|
71
|
+
const sinceStart = BN.max(ZERO, new BN(60 * 5).sub(sinceLastUpdate));
|
|
72
|
+
const oracleTwap5min = amm.historicalOracleData.lastOraclePriceTwap5min
|
|
73
|
+
.mul(sinceStart)
|
|
74
|
+
.add(oraclePriceData.price)
|
|
75
|
+
.mul(sinceLastUpdate)
|
|
76
|
+
.div(sinceStart.add(sinceLastUpdate));
|
|
77
|
+
|
|
78
|
+
const oracleSpread = oracleTwap5min.sub(oraclePriceData.price);
|
|
79
|
+
const oracleSpreadPct = oracleSpread.mul(PRICE_PRECISION).div(oracleTwap5min);
|
|
80
|
+
|
|
81
|
+
const tooDivergent = oracleSpreadPct
|
|
82
|
+
.abs()
|
|
83
|
+
.gte(
|
|
84
|
+
BID_ASK_SPREAD_PRECISION.mul(
|
|
85
|
+
oracleGuardRails.priceDivergence.markOracleDivergenceNumerator
|
|
86
|
+
).div(oracleGuardRails.priceDivergence.markOracleDivergenceDenominator)
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
return tooDivergent;
|
|
90
|
+
}
|
package/src/math/orders.ts
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
import { ClearingHouseUser } from '../clearingHouseUser';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
isOneOfVariant,
|
|
4
|
+
isVariant,
|
|
5
|
+
PerpMarketAccount,
|
|
6
|
+
Order,
|
|
7
|
+
PositionDirection,
|
|
8
|
+
} from '../types';
|
|
3
9
|
import { ZERO, TWO } from '../constants/numericConstants';
|
|
4
10
|
import { BN } from '@project-serum/anchor';
|
|
5
11
|
import { OraclePriceData } from '../oracles/types';
|
|
6
|
-
import { getAuctionPrice } from './auction';
|
|
12
|
+
import { getAuctionPrice, isAuctionComplete } from './auction';
|
|
13
|
+
import { calculateAskPrice, calculateBidPrice } from './market';
|
|
14
|
+
import {
|
|
15
|
+
calculateMaxBaseAssetAmountFillable,
|
|
16
|
+
calculateMaxBaseAssetAmountToTrade,
|
|
17
|
+
} from './amm';
|
|
7
18
|
|
|
8
19
|
export function isOrderRiskIncreasing(
|
|
9
20
|
user: ClearingHouseUser,
|
|
@@ -121,16 +132,139 @@ export function standardizeBaseAssetAmount(
|
|
|
121
132
|
export function getLimitPrice(
|
|
122
133
|
order: Order,
|
|
123
134
|
oraclePriceData: OraclePriceData,
|
|
124
|
-
slot: number
|
|
135
|
+
slot: number,
|
|
136
|
+
perpMarket?: PerpMarketAccount
|
|
125
137
|
): BN {
|
|
126
138
|
let limitPrice;
|
|
127
139
|
if (!order.oraclePriceOffset.eq(ZERO)) {
|
|
128
140
|
limitPrice = oraclePriceData.price.add(order.oraclePriceOffset);
|
|
129
141
|
} else if (isOneOfVariant(order.orderType, ['market', 'triggerMarket'])) {
|
|
130
|
-
|
|
142
|
+
if (!isAuctionComplete(order, slot)) {
|
|
143
|
+
limitPrice = getAuctionPrice(order, slot);
|
|
144
|
+
} else if (!order.price.eq(ZERO)) {
|
|
145
|
+
limitPrice = order.price;
|
|
146
|
+
} else {
|
|
147
|
+
if (perpMarket) {
|
|
148
|
+
if (isVariant(order.direction, 'long')) {
|
|
149
|
+
const askPrice = calculateAskPrice(perpMarket, oraclePriceData);
|
|
150
|
+
const delta = askPrice.div(new BN(perpMarket.amm.maxSlippageRatio));
|
|
151
|
+
limitPrice = askPrice.add(delta);
|
|
152
|
+
} else {
|
|
153
|
+
const bidPrice = calculateBidPrice(perpMarket, oraclePriceData);
|
|
154
|
+
const delta = bidPrice.div(new BN(perpMarket.amm.maxSlippageRatio));
|
|
155
|
+
limitPrice = bidPrice.sub(delta);
|
|
156
|
+
}
|
|
157
|
+
} else {
|
|
158
|
+
// check oracle validity?
|
|
159
|
+
const oraclePrice1Pct = oraclePriceData.price.div(new BN(100));
|
|
160
|
+
if (isVariant(order.direction, 'long')) {
|
|
161
|
+
limitPrice = oraclePriceData.price.add(oraclePrice1Pct);
|
|
162
|
+
} else {
|
|
163
|
+
limitPrice = oraclePriceData.price.sub(oraclePrice1Pct);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
131
167
|
} else {
|
|
132
168
|
limitPrice = order.price;
|
|
133
169
|
}
|
|
134
170
|
|
|
135
171
|
return limitPrice;
|
|
136
172
|
}
|
|
173
|
+
|
|
174
|
+
export function isFillableByVAMM(
|
|
175
|
+
order: Order,
|
|
176
|
+
market: PerpMarketAccount,
|
|
177
|
+
oraclePriceData: OraclePriceData,
|
|
178
|
+
slot: number
|
|
179
|
+
): boolean {
|
|
180
|
+
return (
|
|
181
|
+
(isAuctionComplete(order, slot) &&
|
|
182
|
+
!calculateBaseAssetAmountForAmmToFulfill(
|
|
183
|
+
order,
|
|
184
|
+
market,
|
|
185
|
+
oraclePriceData,
|
|
186
|
+
slot
|
|
187
|
+
).eq(ZERO)) ||
|
|
188
|
+
isOrderExpired(order, slot)
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export function calculateBaseAssetAmountForAmmToFulfill(
|
|
193
|
+
order: Order,
|
|
194
|
+
market: PerpMarketAccount,
|
|
195
|
+
oraclePriceData: OraclePriceData,
|
|
196
|
+
slot: number
|
|
197
|
+
): BN {
|
|
198
|
+
if (
|
|
199
|
+
isOneOfVariant(order.orderType, ['triggerMarket', 'triggerLimit']) &&
|
|
200
|
+
order.triggered === false
|
|
201
|
+
) {
|
|
202
|
+
return ZERO;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const limitPrice = getLimitPrice(order, oraclePriceData, slot, market);
|
|
206
|
+
const baseAssetAmount = calculateBaseAssetAmountToFillUpToLimitPrice(
|
|
207
|
+
order,
|
|
208
|
+
market,
|
|
209
|
+
limitPrice,
|
|
210
|
+
oraclePriceData
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
const maxBaseAssetAmount = calculateMaxBaseAssetAmountFillable(
|
|
214
|
+
market.amm,
|
|
215
|
+
order.direction
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
return BN.min(maxBaseAssetAmount, baseAssetAmount);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export function calculateBaseAssetAmountToFillUpToLimitPrice(
|
|
222
|
+
order: Order,
|
|
223
|
+
market: PerpMarketAccount,
|
|
224
|
+
limitPrice: BN,
|
|
225
|
+
oraclePriceData: OraclePriceData
|
|
226
|
+
): BN {
|
|
227
|
+
const [maxAmountToTrade, direction] = calculateMaxBaseAssetAmountToTrade(
|
|
228
|
+
market.amm,
|
|
229
|
+
limitPrice,
|
|
230
|
+
order.direction,
|
|
231
|
+
oraclePriceData
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
const baseAssetAmount = standardizeBaseAssetAmount(
|
|
235
|
+
maxAmountToTrade,
|
|
236
|
+
market.amm.baseAssetAmountStepSize
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
// Check that directions are the same
|
|
240
|
+
const sameDirection = isSameDirection(direction, order.direction);
|
|
241
|
+
if (!sameDirection) {
|
|
242
|
+
return ZERO;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return baseAssetAmount.gt(order.baseAssetAmount)
|
|
246
|
+
? order.baseAssetAmount
|
|
247
|
+
: baseAssetAmount;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function isSameDirection(
|
|
251
|
+
firstDirection: PositionDirection,
|
|
252
|
+
secondDirection: PositionDirection
|
|
253
|
+
): boolean {
|
|
254
|
+
return (
|
|
255
|
+
(isVariant(firstDirection, 'long') && isVariant(secondDirection, 'long')) ||
|
|
256
|
+
(isVariant(firstDirection, 'short') && isVariant(secondDirection, 'short'))
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export function isOrderExpired(order: Order, slot: number): boolean {
|
|
261
|
+
if (
|
|
262
|
+
isOneOfVariant(order.orderType, ['triggerMarket', 'triggerLimit']) ||
|
|
263
|
+
!isVariant(order.status, 'open') ||
|
|
264
|
+
order.timeInForce === 0
|
|
265
|
+
) {
|
|
266
|
+
return false;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return new BN(slot).sub(order.slot).gt(new BN(order.timeInForce));
|
|
270
|
+
}
|