@velocity-exchange/sdk 0.4.0 → 0.6.1
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 +3 -3
- package/lib/browser/adminClient.d.ts +52 -0
- package/lib/browser/adminClient.js +97 -0
- package/lib/browser/config.js +6 -7
- package/lib/browser/constants/numericConstants.d.ts +1 -1
- package/lib/browser/constants/numericConstants.js +1 -1
- package/lib/browser/decode/user.js +5 -1
- package/lib/browser/idl/velocity.d.ts +191 -15
- package/lib/browser/idl/velocity.json +192 -16
- package/lib/browser/orderSubscriber/OrderSubscriber.d.ts +1 -0
- package/lib/browser/orderSubscriber/OrderSubscriber.js +10 -4
- package/lib/browser/pyth/pythLazerSubscriber.js +37 -27
- package/lib/browser/types.d.ts +6 -0
- package/lib/browser/user.d.ts +20 -3
- package/lib/browser/user.js +52 -4
- package/lib/browser/velocityClient.d.ts +46 -10
- package/lib/browser/velocityClient.js +107 -35
- package/lib/node/adminClient.d.ts +52 -0
- package/lib/node/adminClient.d.ts.map +1 -1
- package/lib/node/adminClient.js +97 -0
- package/lib/node/config.d.ts.map +1 -1
- package/lib/node/config.js +6 -7
- package/lib/node/constants/numericConstants.d.ts +1 -1
- package/lib/node/constants/numericConstants.d.ts.map +1 -1
- package/lib/node/constants/numericConstants.js +1 -1
- package/lib/node/decode/user.d.ts.map +1 -1
- package/lib/node/decode/user.js +5 -1
- package/lib/node/idl/velocity.d.ts +191 -15
- package/lib/node/idl/velocity.d.ts.map +1 -1
- package/lib/node/idl/velocity.json +192 -16
- package/lib/node/orderSubscriber/OrderSubscriber.d.ts +1 -0
- package/lib/node/orderSubscriber/OrderSubscriber.d.ts.map +1 -1
- package/lib/node/orderSubscriber/OrderSubscriber.js +10 -4
- package/lib/node/pyth/pythLazerSubscriber.d.ts.map +1 -1
- package/lib/node/pyth/pythLazerSubscriber.js +37 -27
- package/lib/node/types.d.ts +6 -0
- package/lib/node/types.d.ts.map +1 -1
- package/lib/node/user.d.ts +20 -3
- package/lib/node/user.d.ts.map +1 -1
- package/lib/node/user.js +52 -4
- package/lib/node/velocityClient.d.ts +46 -10
- package/lib/node/velocityClient.d.ts.map +1 -1
- package/lib/node/velocityClient.js +107 -35
- package/package.json +7 -3
- package/.prettierignore +0 -1
- package/CHANGELOG.md +0 -132
- package/build-browser.js +0 -58
- package/bunfig.toml +0 -3
- package/esbuild-shims.js +0 -12
- package/scripts/deposit-isolated-positions.ts +0 -110
- package/scripts/find-flagged-users.ts +0 -213
- package/scripts/grpc-client-test-comparison.ts +0 -372
- package/scripts/grpc-multiuser-client-test-comparison.ts +0 -158
- package/scripts/postbuild.js +0 -95
- package/scripts/single-grpc-client-test.ts +0 -284
- package/scripts/withdraw-isolated-positions.ts +0 -174
- package/src/accounts/README_WebSocketAccountSubscriberV2.md +0 -95
- package/src/accounts/README_WebSocketProgramAccountSubscriberV2.md +0 -135
- package/src/accounts/basicUserAccountSubscriber.ts +0 -79
- package/src/accounts/basicUserStatsAccountSubscriber.ts +0 -87
- package/src/accounts/bulkAccountLoader.ts +0 -358
- package/src/accounts/bulkUserStatsSubscription.ts +0 -40
- package/src/accounts/bulkUserSubscription.ts +0 -39
- package/src/accounts/customizedCadenceBulkAccountLoader.ts +0 -250
- package/src/accounts/fetch.ts +0 -200
- package/src/accounts/grpcAccountSubscriber.ts +0 -225
- package/src/accounts/grpcInsuranceFundStakeAccountSubscriber.ts +0 -73
- package/src/accounts/grpcMultiAccountSubscriber.ts +0 -566
- package/src/accounts/grpcMultiUserAccountSubscriber.ts +0 -328
- package/src/accounts/grpcProgramAccountSubscriber.ts +0 -264
- package/src/accounts/grpcUserAccountSubscriber.ts +0 -63
- package/src/accounts/grpcUserStatsAccountSubscriber.ts +0 -65
- package/src/accounts/grpcVelocityClientAccountSubscriber.ts +0 -257
- package/src/accounts/grpcVelocityClientAccountSubscriberV2.ts +0 -941
- package/src/accounts/laserProgramAccountSubscriber.ts +0 -266
- package/src/accounts/oneShotUserAccountSubscriber.ts +0 -88
- package/src/accounts/oneShotUserStatsAccountSubscriber.ts +0 -94
- package/src/accounts/pollingInsuranceFundStakeAccountSubscriber.ts +0 -223
- package/src/accounts/pollingOracleAccountSubscriber.ts +0 -151
- package/src/accounts/pollingTokenAccountSubscriber.ts +0 -147
- package/src/accounts/pollingUserAccountSubscriber.ts +0 -198
- package/src/accounts/pollingUserStatsAccountSubscriber.ts +0 -180
- package/src/accounts/pollingVelocityClientAccountSubscriber.ts +0 -774
- package/src/accounts/testBulkAccountLoader.ts +0 -59
- package/src/accounts/types.ts +0 -428
- package/src/accounts/utils.ts +0 -90
- package/src/accounts/webSocketAccountSubscriber.ts +0 -304
- package/src/accounts/webSocketAccountSubscriberV2.ts +0 -536
- package/src/accounts/webSocketInsuranceFundStakeAccountSubscriber.ts +0 -171
- package/src/accounts/webSocketProgramAccountSubscriber.ts +0 -244
- package/src/accounts/webSocketProgramAccountSubscriberV2.ts +0 -1048
- package/src/accounts/webSocketProgramAccountsSubscriberV2.ts +0 -1042
- package/src/accounts/webSocketUserAccountSubscriber.ts +0 -139
- package/src/accounts/webSocketUserStatsAccountSubsriber.ts +0 -131
- package/src/accounts/webSocketVelocityClientAccountSubscriber.ts +0 -865
- package/src/accounts/webSocketVelocityClientAccountSubscriberV2.ts +0 -897
- package/src/accounts/websocketProgramUserAccountSubscriber.ts +0 -127
- package/src/addresses/marketAddresses.ts +0 -28
- package/src/addresses/pda.ts +0 -620
- package/src/adminClient.ts +0 -8136
- package/src/assert/assert.ts +0 -5
- package/src/auctionSubscriber/auctionSubscriber.ts +0 -86
- package/src/auctionSubscriber/auctionSubscriberGrpc.ts +0 -99
- package/src/auctionSubscriber/index.ts +0 -9
- package/src/auctionSubscriber/types.ts +0 -30
- package/src/bankrun/bankrunConnection.ts +0 -630
- package/src/blockhashSubscriber/BlockhashSubscriber.ts +0 -160
- package/src/blockhashSubscriber/index.ts +0 -6
- package/src/blockhashSubscriber/types.ts +0 -13
- package/src/clock/clockSubscriber.ts +0 -146
- package/src/config.ts +0 -312
- package/src/constants/index.ts +0 -9
- package/src/constants/numericConstants.ts +0 -194
- package/src/constants/perpMarkets.ts +0 -127
- package/src/constants/spotMarkets.ts +0 -104
- package/src/constants/txConstants.ts +0 -2
- package/src/constituentMap/constituentMap.ts +0 -305
- package/src/constituentMap/pollingConstituentAccountSubscriber.ts +0 -99
- package/src/constituentMap/webSocketConstituentAccountSubscriber.ts +0 -121
- package/src/core/VelocityCore.ts +0 -606
- package/src/core/index.ts +0 -12
- package/src/core/instructions/deposit.ts +0 -64
- package/src/core/instructions/fill.ts +0 -51
- package/src/core/instructions/funding.ts +0 -33
- package/src/core/instructions/liquidation.ts +0 -55
- package/src/core/instructions/orders.ts +0 -84
- package/src/core/instructions/perpOrders.ts +0 -312
- package/src/core/instructions/settlement.ts +0 -40
- package/src/core/instructions/trigger.ts +0 -41
- package/src/core/instructions/withdraw.ts +0 -65
- package/src/core/remainingAccounts.ts +0 -320
- package/src/core/signedMsg.ts +0 -109
- package/src/decode/customCoder.ts +0 -148
- package/src/decode/user.ts +0 -401
- package/src/dlob/DLOB.ts +0 -2793
- package/src/dlob/DLOBNode.ts +0 -305
- package/src/dlob/DLOBSubscriber.ts +0 -269
- package/src/dlob/NodeList.ts +0 -244
- package/src/dlob/orderBookLevels.ts +0 -695
- package/src/dlob/types.ts +0 -37
- package/src/events/eventList.ts +0 -114
- package/src/events/eventSubscriber.ts +0 -521
- package/src/events/eventsServerLogProvider.ts +0 -189
- package/src/events/fetchLogs.ts +0 -219
- package/src/events/parse.ts +0 -271
- package/src/events/pollingLogProvider.ts +0 -113
- package/src/events/sort.ts +0 -51
- package/src/events/txEventCache.ts +0 -92
- package/src/events/types.ts +0 -337
- package/src/events/webSocketLogProvider.ts +0 -152
- package/src/factory/bigNum.ts +0 -709
- package/src/factory/oracleClient.ts +0 -73
- package/src/idl/pyth.json +0 -142
- package/src/idl/pyth.ts +0 -97
- package/src/idl/pyth_solana_receiver.json +0 -628
- package/src/idl/token_faucet.json +0 -229
- package/src/idl/token_faucet.ts +0 -197
- package/src/idl/velocity.json +0 -23974
- package/src/idl/velocity.ts +0 -23980
- package/src/index.ts +0 -150
- package/src/indicative-quotes/indicativeQuotesSender.ts +0 -233
- package/src/isomorphic/README.md +0 -19
- package/src/isomorphic/anchor.browser.ts +0 -44
- package/src/isomorphic/anchor.node.ts +0 -1
- package/src/isomorphic/anchor.ts +0 -1
- package/src/isomorphic/anchor29.browser.ts +0 -24
- package/src/isomorphic/anchor29.node.ts +0 -1
- package/src/isomorphic/anchor29.ts +0 -1
- package/src/isomorphic/grpc.browser.ts +0 -4
- package/src/isomorphic/grpc.node.ts +0 -130
- package/src/isomorphic/grpc.ts +0 -1
- package/src/jupiter/jupiterClient.ts +0 -502
- package/src/keypair.ts +0 -32
- package/src/margin/README.md +0 -138
- package/src/marginCalculation.ts +0 -407
- package/src/marinade/idl/idl.json +0 -1962
- package/src/marinade/index.ts +0 -73
- package/src/marinade/types.ts +0 -3925
- package/src/math/amm.ts +0 -1758
- package/src/math/auction.ts +0 -480
- package/src/math/bankruptcy.ts +0 -138
- package/src/math/builder.ts +0 -130
- package/src/math/conversion.ts +0 -45
- package/src/math/exchangeStatus.ts +0 -261
- package/src/math/funding.ts +0 -498
- package/src/math/insurance.ts +0 -171
- package/src/math/liquidation.ts +0 -433
- package/src/math/margin.ts +0 -327
- package/src/math/market.ts +0 -487
- package/src/math/oracles.ts +0 -477
- package/src/math/orders.ts +0 -500
- package/src/math/position.ts +0 -381
- package/src/math/repeg.ts +0 -223
- package/src/math/spotBalance.ts +0 -996
- package/src/math/spotMarket.ts +0 -115
- package/src/math/spotPosition.ts +0 -263
- package/src/math/state.ts +0 -71
- package/src/math/superStake.ts +0 -602
- package/src/math/tiers.ts +0 -73
- package/src/math/trade.ts +0 -879
- package/src/math/utils.ts +0 -183
- package/src/memcmp.ts +0 -279
- package/src/oracles/oracleClientCache.ts +0 -41
- package/src/oracles/oracleId.ts +0 -99
- package/src/oracles/prelaunchOracleClient.ts +0 -63
- package/src/oracles/pythClient.ts +0 -125
- package/src/oracles/pythLazerClient.ts +0 -141
- package/src/oracles/quoteAssetOracleClient.ts +0 -43
- package/src/oracles/strictOraclePrice.ts +0 -40
- package/src/oracles/types.ts +0 -82
- package/src/oracles/utils.ts +0 -45
- package/src/orderParams.ts +0 -117
- package/src/orderSubscriber/OrderSubscriber.ts +0 -378
- package/src/orderSubscriber/PollingSubscription.ts +0 -50
- package/src/orderSubscriber/WebsocketSubscription.ts +0 -137
- package/src/orderSubscriber/grpcSubscription.ts +0 -155
- package/src/orderSubscriber/index.ts +0 -7
- package/src/orderSubscriber/types.ts +0 -64
- package/src/priorityFee/averageOverSlotsStrategy.ts +0 -17
- package/src/priorityFee/averageStrategy.ts +0 -13
- package/src/priorityFee/ewmaStrategy.ts +0 -51
- package/src/priorityFee/heliusPriorityFeeMethod.ts +0 -68
- package/src/priorityFee/index.ts +0 -16
- package/src/priorityFee/maxOverSlotsStrategy.ts +0 -18
- package/src/priorityFee/maxStrategy.ts +0 -8
- package/src/priorityFee/priorityFeeSubscriber.ts +0 -328
- package/src/priorityFee/priorityFeeSubscriberMap.ts +0 -134
- package/src/priorityFee/solanaPriorityFeeMethod.ts +0 -46
- package/src/priorityFee/types.ts +0 -82
- package/src/priorityFee/velocityPriorityFeeMethod.ts +0 -53
- package/src/pyth/constants.ts +0 -9
- package/src/pyth/index.ts +0 -15
- package/src/pyth/pythLazerSubscriber.ts +0 -365
- package/src/pyth/types.ts +0 -4451
- package/src/pyth/utils.ts +0 -13
- package/src/slot/SlotSubscriber.ts +0 -126
- package/src/slot/SlothashSubscriber.ts +0 -163
- package/src/swap/UnifiedSwapClient.ts +0 -315
- package/src/swift/grpcSignedMsgUserAccountSubscriber.ts +0 -81
- package/src/swift/index.ts +0 -10
- package/src/swift/signedMsgUserAccountSubscriber.ts +0 -251
- package/src/swift/swiftOrderSubscriber.ts +0 -351
- package/src/testClient.ts +0 -42
- package/src/titan/titanClient.ts +0 -438
- package/src/token/index.ts +0 -13
- package/src/tokenFaucet.ts +0 -296
- package/src/tx/baseTxSender.ts +0 -651
- package/src/tx/blockhashFetcher/baseBlockhashFetcher.ts +0 -31
- package/src/tx/blockhashFetcher/cachedBlockhashFetcher.ts +0 -105
- package/src/tx/blockhashFetcher/types.ts +0 -9
- package/src/tx/fastSingleTxSender.ts +0 -200
- package/src/tx/forwardOnlyTxSender.ts +0 -193
- package/src/tx/priorityFeeCalculator.ts +0 -117
- package/src/tx/reportTransactionError.ts +0 -176
- package/src/tx/retryTxSender.ts +0 -177
- package/src/tx/txHandler.ts +0 -1009
- package/src/tx/txParamProcessor.ts +0 -225
- package/src/tx/types.ts +0 -117
- package/src/tx/utils.ts +0 -133
- package/src/tx/whileValidTxSender.ts +0 -377
- package/src/types.ts +0 -2575
- package/src/user.ts +0 -5316
- package/src/userConfig.ts +0 -38
- package/src/userMap/PollingSubscription.ts +0 -61
- package/src/userMap/WebsocketSubscription.ts +0 -93
- package/src/userMap/grpcSubscription.ts +0 -93
- package/src/userMap/referrerMap.ts +0 -360
- package/src/userMap/revenueShareEscrowMap.ts +0 -342
- package/src/userMap/userMap.ts +0 -784
- package/src/userMap/userMapConfig.ts +0 -82
- package/src/userMap/userStatsMap.ts +0 -393
- package/src/userName.ts +0 -37
- package/src/userStats.ts +0 -159
- package/src/userStatsConfig.ts +0 -35
- package/src/util/TransactionConfirmationManager.ts +0 -333
- package/src/util/chainClock.ts +0 -65
- package/src/util/computeUnits.ts +0 -101
- package/src/util/digest.ts +0 -33
- package/src/util/ed25519Utils.ts +0 -95
- package/src/util/promiseTimeout.ts +0 -23
- package/src/velocityClient.ts +0 -14802
- package/src/velocityClientConfig.ts +0 -119
- package/src/wallet.ts +0 -91
- package/tests/VelocityCore/builder_escrow.test.ts +0 -121
- package/tests/VelocityCore/decode.test.ts +0 -16
- package/tests/VelocityCore/fill_trigger.test.ts +0 -73
- package/tests/VelocityCore/instructions.test.ts +0 -46
- package/tests/VelocityCore/pdas.test.ts +0 -30
- package/tests/VelocityCore/perp_orders.test.ts +0 -205
- package/tests/VelocityCore/remainingAccounts.test.ts +0 -72
- package/tests/VelocityCore/settlement_liquidation.test.ts +0 -69
- package/tests/accounts/customizedCadenceBulkAccountLoader.test.ts +0 -211
- package/tests/amm/test.ts +0 -2402
- package/tests/auctions/test.ts +0 -81
- package/tests/bn/test.ts +0 -355
- package/tests/builder/builderFee.test.ts +0 -42
- package/tests/ci/verifyConstants.ts +0 -393
- package/tests/decode/test.ts +0 -262
- package/tests/decode/userAccountBufferStrings.ts +0 -102
- package/tests/dlob/helpers.ts +0 -748
- package/tests/dlob/test.ts +0 -6969
- package/tests/dlob/tickSizeStandardization.ts +0 -545
- package/tests/events/parseLogsForCuUsage.ts +0 -139
- package/tests/exchangeStatus/test.ts +0 -45
- package/tests/insurance/test.ts +0 -40
- package/tests/liquidation/test.ts +0 -125
- package/tests/oracles/mmOracleGate.test.ts +0 -379
- package/tests/oracles/pythPegSnap.test.ts +0 -76
- package/tests/sdkParity/enumParity.test.ts +0 -84
- package/tests/sdkParity/marginCategoryFill.test.ts +0 -143
- package/tests/sdkParity/memcmpOffsets.test.ts +0 -139
- package/tests/spot/test.ts +0 -281
- package/tests/tx/TransactionConfirmationManager.test.ts +0 -312
- package/tests/tx/cachedBlockhashFetcher.test.ts +0 -96
- package/tests/tx/priorityFeeCalculator.ts +0 -77
- package/tests/tx/priorityFeeStrategy.ts +0 -95
- package/tests/user/bankruptcy.ts +0 -165
- package/tests/user/feeAndWithdrawLimits.ts +0 -284
- package/tests/user/getMarginCalculation.ts +0 -586
- package/tests/user/helpers.ts +0 -184
- package/tests/user/liquidations.ts +0 -129
- package/tests/user/marginCalculations.test.ts +0 -321
- package/tests/user/test.ts +0 -833
- package/tsconfig.browser.json +0 -18
- package/tsconfig.json +0 -36
package/src/math/trade.ts
DELETED
|
@@ -1,879 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
MarketType,
|
|
3
|
-
PerpMarketAccount,
|
|
4
|
-
PositionDirection,
|
|
5
|
-
UserStatsAccount,
|
|
6
|
-
} from '../types';
|
|
7
|
-
import { BN } from '../isomorphic/anchor';
|
|
8
|
-
import { assert } from '../assert/assert';
|
|
9
|
-
import {
|
|
10
|
-
PRICE_PRECISION,
|
|
11
|
-
PEG_PRECISION,
|
|
12
|
-
AMM_TO_QUOTE_PRECISION_RATIO,
|
|
13
|
-
ZERO,
|
|
14
|
-
BASE_PRECISION,
|
|
15
|
-
BN_MAX,
|
|
16
|
-
} from '../constants/numericConstants';
|
|
17
|
-
import {
|
|
18
|
-
calculateBidPrice,
|
|
19
|
-
calculateAskPrice,
|
|
20
|
-
calculateReservePrice,
|
|
21
|
-
} from './market';
|
|
22
|
-
import {
|
|
23
|
-
calculateAmmReservesAfterSwap,
|
|
24
|
-
calculatePrice,
|
|
25
|
-
getSwapDirection,
|
|
26
|
-
AssetType,
|
|
27
|
-
calculateUpdatedAMMSpreadReserves,
|
|
28
|
-
calculateQuoteAssetAmountSwapped,
|
|
29
|
-
calculateMarketOpenBidAsk,
|
|
30
|
-
} from './amm';
|
|
31
|
-
import { squareRootBN } from './utils';
|
|
32
|
-
import { isVariant } from '../types';
|
|
33
|
-
import { MMOraclePriceData } from '../oracles/types';
|
|
34
|
-
import { DLOB } from '../dlob/DLOB';
|
|
35
|
-
import { PublicKey } from '@solana/web3.js';
|
|
36
|
-
import { L2OrderBook } from '../dlob/orderBookLevels';
|
|
37
|
-
|
|
38
|
-
const MAXPCT = new BN(1000); //percentage units are [0,1000] => [0,1]
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Enumerates the price-impact-related fields historically produced by trade-slippage helpers.
|
|
42
|
-
* Not currently consumed as a parameter/return type by any function in this file — kept for
|
|
43
|
-
* backward compatibility with callers that reference it as a key type.
|
|
44
|
-
*/
|
|
45
|
-
export type PriceImpactUnit =
|
|
46
|
-
| 'entryPrice'
|
|
47
|
-
| 'maxPrice'
|
|
48
|
-
| 'priceDelta'
|
|
49
|
-
| 'priceDeltaAsNumber'
|
|
50
|
-
| 'pctAvg'
|
|
51
|
-
| 'pctMax'
|
|
52
|
-
| 'quoteAssetAmount'
|
|
53
|
-
| 'quoteAssetAmountPeg'
|
|
54
|
-
| 'acquiredBaseAssetAmount'
|
|
55
|
-
| 'acquiredQuoteAssetAmount'
|
|
56
|
-
| 'all';
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Calculates avg/max slippage (price impact) for a hypothetical AMM-only trade.
|
|
60
|
-
*
|
|
61
|
-
* @deprecated Use `calculateEstimatedPerpEntryPrice` instead (this ignores DLOB liquidity and
|
|
62
|
-
* only swaps against the vAMM).
|
|
63
|
-
*
|
|
64
|
-
* @param {PositionDirection} direction - Taker's trade direction
|
|
65
|
-
* @param {BN} amount - Trade size in `inputAssetType` units (base: BASE_PRECISION (1e9); quote: QUOTE_PRECISION (1e6))
|
|
66
|
-
* @param {PerpMarketAccount} market - The perp market account
|
|
67
|
-
* @param {AssetType} [inputAssetType] - Whether `amount` denominates base or quote; defaults to `'quote'`
|
|
68
|
-
* @param {MMOraclePriceData} mmOraclePriceData - MM oracle price data used for spread reserve calc
|
|
69
|
-
* @param {boolean} [useSpread] - Whether to consider the bid/ask spread when computing slippage; defaults to `true`
|
|
70
|
-
* @param {BN} [latestSlot] - Slot used for spread-reserve staleness/decay calc when `useSpread` is true
|
|
71
|
-
* @return {[BN, BN, BN, BN]} `[pctAvgSlippage, pctMaxSlippage, entryPrice, newPrice]`, all
|
|
72
|
-
* PRICE_PRECISION (1e6): `pctAvgSlippage` is the percentage change from the pre-trade price to
|
|
73
|
-
* `entryPrice` (average execution slippage); `pctMaxSlippage` is the percentage change from the
|
|
74
|
-
* pre-trade price to `newPrice` (worst-case/marginal slippage); `entryPrice` is the trade's
|
|
75
|
-
* average execution price; `newPrice` is the AMM's price after the trade
|
|
76
|
-
*/
|
|
77
|
-
export function calculateTradeSlippage(
|
|
78
|
-
direction: PositionDirection,
|
|
79
|
-
amount: BN,
|
|
80
|
-
market: PerpMarketAccount,
|
|
81
|
-
inputAssetType: AssetType = 'quote',
|
|
82
|
-
mmOraclePriceData: MMOraclePriceData,
|
|
83
|
-
useSpread = true,
|
|
84
|
-
latestSlot?: BN
|
|
85
|
-
): [BN, BN, BN, BN] {
|
|
86
|
-
let oldPrice: BN;
|
|
87
|
-
|
|
88
|
-
if (useSpread && market.amm.baseSpread > 0) {
|
|
89
|
-
if (isVariant(direction, 'long')) {
|
|
90
|
-
oldPrice = calculateAskPrice(market, mmOraclePriceData);
|
|
91
|
-
} else {
|
|
92
|
-
oldPrice = calculateBidPrice(market, mmOraclePriceData);
|
|
93
|
-
}
|
|
94
|
-
} else {
|
|
95
|
-
oldPrice = calculateReservePrice(market, mmOraclePriceData);
|
|
96
|
-
}
|
|
97
|
-
if (amount.eq(ZERO)) {
|
|
98
|
-
return [ZERO, ZERO, oldPrice, oldPrice];
|
|
99
|
-
}
|
|
100
|
-
const [acquiredBaseReserve, acquiredQuoteReserve, acquiredQuoteAssetAmount] =
|
|
101
|
-
calculateTradeAcquiredAmounts(
|
|
102
|
-
direction,
|
|
103
|
-
amount,
|
|
104
|
-
market,
|
|
105
|
-
inputAssetType,
|
|
106
|
-
mmOraclePriceData,
|
|
107
|
-
useSpread
|
|
108
|
-
);
|
|
109
|
-
|
|
110
|
-
const entryPrice = acquiredQuoteAssetAmount
|
|
111
|
-
.mul(AMM_TO_QUOTE_PRECISION_RATIO)
|
|
112
|
-
.mul(PRICE_PRECISION)
|
|
113
|
-
.div(acquiredBaseReserve.abs());
|
|
114
|
-
|
|
115
|
-
let amm: Parameters<typeof calculateAmmReservesAfterSwap>[0];
|
|
116
|
-
if (useSpread && market.amm.baseSpread > 0) {
|
|
117
|
-
const { baseAssetReserve, quoteAssetReserve, sqrtK, newPeg } =
|
|
118
|
-
calculateUpdatedAMMSpreadReserves(
|
|
119
|
-
market.amm,
|
|
120
|
-
market.marketStats,
|
|
121
|
-
direction,
|
|
122
|
-
mmOraclePriceData,
|
|
123
|
-
latestSlot
|
|
124
|
-
);
|
|
125
|
-
amm = {
|
|
126
|
-
baseAssetReserve,
|
|
127
|
-
quoteAssetReserve,
|
|
128
|
-
sqrtK: sqrtK,
|
|
129
|
-
pegMultiplier: newPeg,
|
|
130
|
-
};
|
|
131
|
-
} else {
|
|
132
|
-
amm = market.amm;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
const newPrice = calculatePrice(
|
|
136
|
-
amm.baseAssetReserve.sub(acquiredBaseReserve),
|
|
137
|
-
amm.quoteAssetReserve.sub(acquiredQuoteReserve),
|
|
138
|
-
amm.pegMultiplier
|
|
139
|
-
);
|
|
140
|
-
|
|
141
|
-
if (direction == PositionDirection.SHORT) {
|
|
142
|
-
assert(newPrice.lte(oldPrice));
|
|
143
|
-
} else {
|
|
144
|
-
assert(oldPrice.lte(newPrice));
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
const pctMaxSlippage = newPrice
|
|
148
|
-
.sub(oldPrice)
|
|
149
|
-
.mul(PRICE_PRECISION)
|
|
150
|
-
.div(oldPrice)
|
|
151
|
-
.abs();
|
|
152
|
-
const pctAvgSlippage = entryPrice
|
|
153
|
-
.sub(oldPrice)
|
|
154
|
-
.mul(PRICE_PRECISION)
|
|
155
|
-
.div(oldPrice)
|
|
156
|
-
.abs();
|
|
157
|
-
|
|
158
|
-
return [pctAvgSlippage, pctMaxSlippage, entryPrice, newPrice];
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Calculates the AMM reserve deltas and resulting quote amount for a hypothetical constant-product
|
|
163
|
-
* swap against the vAMM, without executing anything on-chain.
|
|
164
|
-
*
|
|
165
|
-
* @param {PositionDirection} direction - Taker's trade direction
|
|
166
|
-
* @param {BN} amount - Trade size in `inputAssetType` units (base: BASE_PRECISION (1e9); quote: QUOTE_PRECISION (1e6))
|
|
167
|
-
* @param {PerpMarketAccount} market - The perp market account
|
|
168
|
-
* @param {AssetType} [inputAssetType] - Whether `amount` denominates base or quote; defaults to `'quote'`
|
|
169
|
-
* @param {MMOraclePriceData} mmOraclePriceData - MM oracle price data used for spread reserve calc
|
|
170
|
-
* @param {boolean} [useSpread] - Whether to swap against the spread-adjusted reserves (bid/ask)
|
|
171
|
-
* rather than the raw reserves; defaults to `true`
|
|
172
|
-
* @param {BN} [latestSlot] - Slot used for spread-reserve staleness/decay calc when `useSpread` is true
|
|
173
|
-
* @return {[BN, BN, BN]} `[acquiredBase, acquiredQuote, acquiredQuoteAssetAmount]` — the change
|
|
174
|
-
* in the AMM's base and quote reserves (signed, `AMM_RESERVE_PRECISION` (1e9)), and the
|
|
175
|
-
* resulting user-facing quote amount swapped, `QUOTE_PRECISION` (1e6)
|
|
176
|
-
*/
|
|
177
|
-
export function calculateTradeAcquiredAmounts(
|
|
178
|
-
direction: PositionDirection,
|
|
179
|
-
amount: BN,
|
|
180
|
-
market: PerpMarketAccount,
|
|
181
|
-
inputAssetType: AssetType = 'quote',
|
|
182
|
-
mmOraclePriceData: MMOraclePriceData,
|
|
183
|
-
useSpread = true,
|
|
184
|
-
latestSlot?: BN
|
|
185
|
-
): [BN, BN, BN] {
|
|
186
|
-
if (amount.eq(ZERO)) {
|
|
187
|
-
return [ZERO, ZERO, ZERO];
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
const swapDirection = getSwapDirection(inputAssetType, direction);
|
|
191
|
-
|
|
192
|
-
let amm: Parameters<typeof calculateAmmReservesAfterSwap>[0];
|
|
193
|
-
if (useSpread && market.amm.baseSpread > 0) {
|
|
194
|
-
const { baseAssetReserve, quoteAssetReserve, sqrtK, newPeg } =
|
|
195
|
-
calculateUpdatedAMMSpreadReserves(
|
|
196
|
-
market.amm,
|
|
197
|
-
market.marketStats,
|
|
198
|
-
direction,
|
|
199
|
-
mmOraclePriceData,
|
|
200
|
-
latestSlot
|
|
201
|
-
);
|
|
202
|
-
amm = {
|
|
203
|
-
baseAssetReserve,
|
|
204
|
-
quoteAssetReserve,
|
|
205
|
-
sqrtK: sqrtK,
|
|
206
|
-
pegMultiplier: newPeg,
|
|
207
|
-
};
|
|
208
|
-
} else {
|
|
209
|
-
amm = market.amm;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
const [newQuoteAssetReserve, newBaseAssetReserve] =
|
|
213
|
-
calculateAmmReservesAfterSwap(amm, inputAssetType, amount, swapDirection);
|
|
214
|
-
|
|
215
|
-
const acquiredBase = amm.baseAssetReserve.sub(newBaseAssetReserve);
|
|
216
|
-
const acquiredQuote = amm.quoteAssetReserve.sub(newQuoteAssetReserve);
|
|
217
|
-
const acquiredQuoteAssetAmount = calculateQuoteAssetAmountSwapped(
|
|
218
|
-
acquiredQuote.abs(),
|
|
219
|
-
amm.pegMultiplier,
|
|
220
|
-
swapDirection
|
|
221
|
-
);
|
|
222
|
-
|
|
223
|
-
return [acquiredBase, acquiredQuote, acquiredQuoteAssetAmount];
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* Calculates the AMM-only trade (direction + size) required to push the market's reserve price
|
|
228
|
-
* to (or `pct` of the way to) `targetPrice` — a simple arbitrage-sizing helper.
|
|
229
|
-
*
|
|
230
|
-
* @deprecated No longer actively maintained; ignores DLOB liquidity.
|
|
231
|
-
*
|
|
232
|
-
* @param {PerpMarketAccount} market - The perp market account
|
|
233
|
-
* @param {BN} targetPrice - The price to arbitrage toward, PRICE_PRECISION (1e6)
|
|
234
|
-
* @param {BN} [pct] - Fraction of the full price gap to close, out of `MAXPCT` (1000 = 100%);
|
|
235
|
-
* defaults to fully closing the gap
|
|
236
|
-
* @param {AssetType} [outputAssetType] - Whether the returned trade size is denominated in base
|
|
237
|
-
* or quote; defaults to `'quote'`
|
|
238
|
-
* @param {MMOraclePriceData} [mmOraclePriceData] - MM oracle price data used for spread reserve calc
|
|
239
|
-
* @param {boolean} [useSpread] - Whether to consider the bid/ask spread when sizing the trade;
|
|
240
|
-
* defaults to `true`. If `targetPrice` already sits within the current bid/ask spread, returns
|
|
241
|
-
* a zero-size trade
|
|
242
|
-
* @param {BN} [latestSlot] - Slot used for spread-reserve staleness/decay calc when `useSpread` is true
|
|
243
|
-
* @return {[PositionDirection, BN, BN, BN]} `[direction, tradeSize, entryPrice, targetPrice]` —
|
|
244
|
-
* `direction` required to move price toward `targetPrice`; `tradeSize` in `outputAssetType`
|
|
245
|
-
* units (base: BASE_PRECISION (1e9); quote: QUOTE_PRECISION (1e6)); `entryPrice`/`targetPrice`
|
|
246
|
-
* PRICE_PRECISION (1e6)
|
|
247
|
-
*/
|
|
248
|
-
export function calculateTargetPriceTrade(
|
|
249
|
-
market: PerpMarketAccount,
|
|
250
|
-
targetPrice: BN,
|
|
251
|
-
pct: BN = MAXPCT,
|
|
252
|
-
outputAssetType: AssetType = 'quote',
|
|
253
|
-
mmOraclePriceData?: MMOraclePriceData,
|
|
254
|
-
useSpread = true,
|
|
255
|
-
latestSlot?: BN
|
|
256
|
-
): [PositionDirection, BN, BN, BN] {
|
|
257
|
-
assert(market.amm.baseAssetReserve.gt(ZERO));
|
|
258
|
-
assert(targetPrice.gt(ZERO));
|
|
259
|
-
assert(pct.lte(MAXPCT) && pct.gt(ZERO));
|
|
260
|
-
|
|
261
|
-
const reservePriceBefore = calculateReservePrice(market, mmOraclePriceData);
|
|
262
|
-
const bidPriceBefore = calculateBidPrice(market, mmOraclePriceData);
|
|
263
|
-
const askPriceBefore = calculateAskPrice(market, mmOraclePriceData);
|
|
264
|
-
|
|
265
|
-
let direction;
|
|
266
|
-
if (targetPrice.gt(reservePriceBefore)) {
|
|
267
|
-
const priceGap = targetPrice.sub(reservePriceBefore);
|
|
268
|
-
const priceGapScaled = priceGap.mul(pct).div(MAXPCT);
|
|
269
|
-
targetPrice = reservePriceBefore.add(priceGapScaled);
|
|
270
|
-
direction = PositionDirection.LONG;
|
|
271
|
-
} else {
|
|
272
|
-
const priceGap = reservePriceBefore.sub(targetPrice);
|
|
273
|
-
const priceGapScaled = priceGap.mul(pct).div(MAXPCT);
|
|
274
|
-
targetPrice = reservePriceBefore.sub(priceGapScaled);
|
|
275
|
-
direction = PositionDirection.SHORT;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
let tradeSize;
|
|
279
|
-
let baseSize;
|
|
280
|
-
|
|
281
|
-
let baseAssetReserveBefore: BN;
|
|
282
|
-
let quoteAssetReserveBefore: BN;
|
|
283
|
-
|
|
284
|
-
let peg = market.amm.pegMultiplier;
|
|
285
|
-
|
|
286
|
-
if (useSpread && market.amm.baseSpread > 0) {
|
|
287
|
-
const { baseAssetReserve, quoteAssetReserve, newPeg } =
|
|
288
|
-
calculateUpdatedAMMSpreadReserves(
|
|
289
|
-
market.amm,
|
|
290
|
-
market.marketStats,
|
|
291
|
-
direction,
|
|
292
|
-
mmOraclePriceData,
|
|
293
|
-
latestSlot
|
|
294
|
-
);
|
|
295
|
-
baseAssetReserveBefore = baseAssetReserve;
|
|
296
|
-
quoteAssetReserveBefore = quoteAssetReserve;
|
|
297
|
-
peg = newPeg;
|
|
298
|
-
} else {
|
|
299
|
-
baseAssetReserveBefore = market.amm.baseAssetReserve;
|
|
300
|
-
quoteAssetReserveBefore = market.amm.quoteAssetReserve;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
const invariant = market.amm.sqrtK.mul(market.amm.sqrtK);
|
|
304
|
-
const k = invariant.mul(PRICE_PRECISION);
|
|
305
|
-
|
|
306
|
-
let baseAssetReserveAfter;
|
|
307
|
-
let quoteAssetReserveAfter;
|
|
308
|
-
const biasModifier = new BN(1);
|
|
309
|
-
let markPriceAfter;
|
|
310
|
-
|
|
311
|
-
if (
|
|
312
|
-
useSpread &&
|
|
313
|
-
targetPrice.lt(askPriceBefore) &&
|
|
314
|
-
targetPrice.gt(bidPriceBefore)
|
|
315
|
-
) {
|
|
316
|
-
// no trade, market is at target
|
|
317
|
-
if (reservePriceBefore.gt(targetPrice)) {
|
|
318
|
-
direction = PositionDirection.SHORT;
|
|
319
|
-
} else {
|
|
320
|
-
direction = PositionDirection.LONG;
|
|
321
|
-
}
|
|
322
|
-
tradeSize = ZERO;
|
|
323
|
-
return [direction, tradeSize, targetPrice, targetPrice];
|
|
324
|
-
} else if (reservePriceBefore.gt(targetPrice)) {
|
|
325
|
-
// overestimate y2
|
|
326
|
-
baseAssetReserveAfter = squareRootBN(
|
|
327
|
-
k.div(targetPrice).mul(peg).div(PEG_PRECISION).sub(biasModifier)
|
|
328
|
-
).sub(new BN(1));
|
|
329
|
-
quoteAssetReserveAfter = k.div(PRICE_PRECISION).div(baseAssetReserveAfter);
|
|
330
|
-
|
|
331
|
-
markPriceAfter = calculatePrice(
|
|
332
|
-
baseAssetReserveAfter,
|
|
333
|
-
quoteAssetReserveAfter,
|
|
334
|
-
peg
|
|
335
|
-
);
|
|
336
|
-
direction = PositionDirection.SHORT;
|
|
337
|
-
tradeSize = quoteAssetReserveBefore
|
|
338
|
-
.sub(quoteAssetReserveAfter)
|
|
339
|
-
.mul(peg)
|
|
340
|
-
.div(PEG_PRECISION)
|
|
341
|
-
.div(AMM_TO_QUOTE_PRECISION_RATIO);
|
|
342
|
-
baseSize = baseAssetReserveAfter.sub(baseAssetReserveBefore);
|
|
343
|
-
} else if (reservePriceBefore.lt(targetPrice)) {
|
|
344
|
-
// underestimate y2
|
|
345
|
-
baseAssetReserveAfter = squareRootBN(
|
|
346
|
-
k.div(targetPrice).mul(peg).div(PEG_PRECISION).add(biasModifier)
|
|
347
|
-
).add(new BN(1));
|
|
348
|
-
quoteAssetReserveAfter = k.div(PRICE_PRECISION).div(baseAssetReserveAfter);
|
|
349
|
-
|
|
350
|
-
markPriceAfter = calculatePrice(
|
|
351
|
-
baseAssetReserveAfter,
|
|
352
|
-
quoteAssetReserveAfter,
|
|
353
|
-
peg
|
|
354
|
-
);
|
|
355
|
-
|
|
356
|
-
direction = PositionDirection.LONG;
|
|
357
|
-
tradeSize = quoteAssetReserveAfter
|
|
358
|
-
.sub(quoteAssetReserveBefore)
|
|
359
|
-
.mul(peg)
|
|
360
|
-
.div(PEG_PRECISION)
|
|
361
|
-
.div(AMM_TO_QUOTE_PRECISION_RATIO);
|
|
362
|
-
baseSize = baseAssetReserveBefore.sub(baseAssetReserveAfter);
|
|
363
|
-
} else {
|
|
364
|
-
// no trade, market is at target
|
|
365
|
-
direction = PositionDirection.LONG;
|
|
366
|
-
tradeSize = ZERO;
|
|
367
|
-
return [direction, tradeSize, targetPrice, targetPrice];
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
let tp1 = targetPrice;
|
|
371
|
-
let tp2 = markPriceAfter;
|
|
372
|
-
let originalDiff = targetPrice.sub(reservePriceBefore);
|
|
373
|
-
|
|
374
|
-
if (direction == PositionDirection.SHORT) {
|
|
375
|
-
tp1 = markPriceAfter;
|
|
376
|
-
tp2 = targetPrice;
|
|
377
|
-
originalDiff = reservePriceBefore.sub(targetPrice);
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
const entryPrice = tradeSize
|
|
381
|
-
.mul(AMM_TO_QUOTE_PRECISION_RATIO)
|
|
382
|
-
.mul(PRICE_PRECISION)
|
|
383
|
-
.div(baseSize.abs());
|
|
384
|
-
|
|
385
|
-
assert(tp1.sub(tp2).lte(originalDiff), 'Target Price Calculation incorrect');
|
|
386
|
-
assert(
|
|
387
|
-
tp2.lte(tp1) || tp2.sub(tp1).abs().ltn(100000),
|
|
388
|
-
'Target Price Calculation incorrect' +
|
|
389
|
-
tp2.toString() +
|
|
390
|
-
'>=' +
|
|
391
|
-
tp1.toString() +
|
|
392
|
-
'err: ' +
|
|
393
|
-
tp2.sub(tp1).abs().toString()
|
|
394
|
-
);
|
|
395
|
-
if (outputAssetType == 'quote') {
|
|
396
|
-
return [direction, tradeSize, entryPrice, targetPrice];
|
|
397
|
-
} else {
|
|
398
|
-
return [direction, baseSize, entryPrice, targetPrice];
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
/**
|
|
403
|
-
* Simulates walking the combined DLOB + vAMM liquidity to estimate the entry price and price
|
|
404
|
-
* impact of a hypothetical taker order, filling against resting limit orders and the AMM's
|
|
405
|
-
* spread-adjusted reserves in whichever is cheaper at each step. Price impact is the difference
|
|
406
|
-
* between the estimated entry price and the best available price (top of book/AMM) before any
|
|
407
|
-
* fill.
|
|
408
|
-
*
|
|
409
|
-
* @param {AssetType} assetType - Whether `amount` denominates base or quote
|
|
410
|
-
* @param {BN} amount - Order size, `assetType === 'base'`: BASE_PRECISION (1e9); `'quote'`: QUOTE_PRECISION (1e6)
|
|
411
|
-
* @param {PositionDirection} direction - Taker's trade direction
|
|
412
|
-
* @param {PerpMarketAccount} market - The perp market account
|
|
413
|
-
* @param {MMOraclePriceData} mmOraclePriceData - MM oracle price data used to price both the DLOB
|
|
414
|
-
* resting orders and the AMM's spread-adjusted reserves
|
|
415
|
-
* @param {DLOB} dlob - The order book to walk for resting limit orders
|
|
416
|
-
* @param {number} slot - Current slot, used to resolve oracle-pegged/auction limit order prices
|
|
417
|
-
* @param {Map<PublicKey, boolean>} [usersToSkip] - Maker user accounts to exclude from the fill
|
|
418
|
-
* simulation (e.g. the taker's own resting orders); defaults to none
|
|
419
|
-
* @return {{ entryPrice: BN; priceImpact: BN; bestPrice: BN; worstPrice: BN; baseFilled: BN;
|
|
420
|
-
* quoteFilled: BN }} `entryPrice`/`bestPrice`/`worstPrice` are PRICE_PRECISION (1e6);
|
|
421
|
-
* `priceImpact` is `|entryPrice - bestPrice| / bestPrice`, also scaled by PRICE_PRECISION
|
|
422
|
-
* (1e6) but represents a ratio, not a price (e.g. `1e4` = 1% impact); `baseFilled` is
|
|
423
|
-
* BASE_PRECISION (1e9); `quoteFilled` is QUOTE_PRECISION (1e6). All-zero only if `amount` is
|
|
424
|
-
* zero; if liquidity runs out before `amount` fully fills, the returned fields reflect the
|
|
425
|
-
* partial fill
|
|
426
|
-
*/
|
|
427
|
-
export function calculateEstimatedPerpEntryPrice(
|
|
428
|
-
assetType: AssetType,
|
|
429
|
-
amount: BN,
|
|
430
|
-
direction: PositionDirection,
|
|
431
|
-
market: PerpMarketAccount,
|
|
432
|
-
mmOraclePriceData: MMOraclePriceData,
|
|
433
|
-
dlob: DLOB,
|
|
434
|
-
slot: number,
|
|
435
|
-
usersToSkip = new Map<PublicKey, boolean>()
|
|
436
|
-
): {
|
|
437
|
-
entryPrice: BN;
|
|
438
|
-
priceImpact: BN;
|
|
439
|
-
bestPrice: BN;
|
|
440
|
-
worstPrice: BN;
|
|
441
|
-
baseFilled: BN;
|
|
442
|
-
quoteFilled: BN;
|
|
443
|
-
} {
|
|
444
|
-
if (amount.eq(ZERO)) {
|
|
445
|
-
return {
|
|
446
|
-
entryPrice: ZERO,
|
|
447
|
-
priceImpact: ZERO,
|
|
448
|
-
bestPrice: ZERO,
|
|
449
|
-
worstPrice: ZERO,
|
|
450
|
-
baseFilled: ZERO,
|
|
451
|
-
quoteFilled: ZERO,
|
|
452
|
-
};
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
const takerIsLong = isVariant(direction, 'long');
|
|
456
|
-
const limitOrders = dlob[
|
|
457
|
-
takerIsLong ? 'getRestingLimitAsks' : 'getRestingLimitBids'
|
|
458
|
-
](
|
|
459
|
-
market.marketIndex,
|
|
460
|
-
slot,
|
|
461
|
-
MarketType.PERP,
|
|
462
|
-
mmOraclePriceData,
|
|
463
|
-
undefined,
|
|
464
|
-
market.orderTickSize
|
|
465
|
-
);
|
|
466
|
-
|
|
467
|
-
const swapDirection = getSwapDirection(assetType, direction);
|
|
468
|
-
|
|
469
|
-
const { baseAssetReserve, quoteAssetReserve, sqrtK, newPeg } =
|
|
470
|
-
calculateUpdatedAMMSpreadReserves(
|
|
471
|
-
market.amm,
|
|
472
|
-
market.marketStats,
|
|
473
|
-
direction,
|
|
474
|
-
mmOraclePriceData,
|
|
475
|
-
new BN(slot)
|
|
476
|
-
);
|
|
477
|
-
const amm = {
|
|
478
|
-
baseAssetReserve,
|
|
479
|
-
quoteAssetReserve,
|
|
480
|
-
sqrtK: sqrtK,
|
|
481
|
-
pegMultiplier: newPeg,
|
|
482
|
-
};
|
|
483
|
-
|
|
484
|
-
const [ammBids, ammAsks] = calculateMarketOpenBidAsk(
|
|
485
|
-
market.amm.baseAssetReserve,
|
|
486
|
-
market.amm.minBaseAssetReserve,
|
|
487
|
-
market.amm.maxBaseAssetReserve,
|
|
488
|
-
market.orderStepSize
|
|
489
|
-
);
|
|
490
|
-
|
|
491
|
-
let ammLiquidity: BN;
|
|
492
|
-
if (assetType === 'base') {
|
|
493
|
-
ammLiquidity = takerIsLong ? ammAsks.abs() : ammBids;
|
|
494
|
-
} else {
|
|
495
|
-
const [afterSwapQuoteReserves, _] = calculateAmmReservesAfterSwap(
|
|
496
|
-
amm,
|
|
497
|
-
'base',
|
|
498
|
-
takerIsLong ? ammAsks.abs() : ammBids,
|
|
499
|
-
getSwapDirection('base', direction)
|
|
500
|
-
);
|
|
501
|
-
|
|
502
|
-
ammLiquidity = calculateQuoteAssetAmountSwapped(
|
|
503
|
-
amm.quoteAssetReserve.sub(afterSwapQuoteReserves).abs(),
|
|
504
|
-
amm.pegMultiplier,
|
|
505
|
-
swapDirection
|
|
506
|
-
);
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
const invariant = amm.sqrtK.mul(amm.sqrtK);
|
|
510
|
-
|
|
511
|
-
let bestPrice = calculatePrice(
|
|
512
|
-
amm.baseAssetReserve,
|
|
513
|
-
amm.quoteAssetReserve,
|
|
514
|
-
amm.pegMultiplier
|
|
515
|
-
);
|
|
516
|
-
|
|
517
|
-
let cumulativeBaseFilled = ZERO;
|
|
518
|
-
let cumulativeQuoteFilled = ZERO;
|
|
519
|
-
|
|
520
|
-
let limitOrder = limitOrders.next().value;
|
|
521
|
-
if (limitOrder) {
|
|
522
|
-
const limitOrderPrice = limitOrder.getPriceOrThrow(
|
|
523
|
-
mmOraclePriceData,
|
|
524
|
-
slot,
|
|
525
|
-
market.orderTickSize
|
|
526
|
-
);
|
|
527
|
-
bestPrice = takerIsLong
|
|
528
|
-
? BN.min(limitOrderPrice, bestPrice)
|
|
529
|
-
: BN.max(limitOrderPrice, bestPrice);
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
let worstPrice = bestPrice;
|
|
533
|
-
|
|
534
|
-
if (assetType === 'base') {
|
|
535
|
-
while (
|
|
536
|
-
!cumulativeBaseFilled.eq(amount) &&
|
|
537
|
-
(ammLiquidity.gt(ZERO) || limitOrder)
|
|
538
|
-
) {
|
|
539
|
-
const limitOrderPrice = limitOrder?.getPrice(
|
|
540
|
-
mmOraclePriceData,
|
|
541
|
-
slot,
|
|
542
|
-
market.orderTickSize
|
|
543
|
-
);
|
|
544
|
-
|
|
545
|
-
let maxAmmFill: BN;
|
|
546
|
-
if (limitOrderPrice) {
|
|
547
|
-
const newBaseReserves = squareRootBN(
|
|
548
|
-
invariant
|
|
549
|
-
.mul(PRICE_PRECISION)
|
|
550
|
-
.mul(amm.pegMultiplier)
|
|
551
|
-
.div(limitOrderPrice)
|
|
552
|
-
.div(PEG_PRECISION)
|
|
553
|
-
);
|
|
554
|
-
|
|
555
|
-
// will be zero if the limit order price is better than the amm price
|
|
556
|
-
maxAmmFill = takerIsLong
|
|
557
|
-
? amm.baseAssetReserve.sub(newBaseReserves)
|
|
558
|
-
: newBaseReserves.sub(amm.baseAssetReserve);
|
|
559
|
-
} else {
|
|
560
|
-
maxAmmFill = amount.sub(cumulativeBaseFilled);
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
maxAmmFill = BN.min(maxAmmFill, ammLiquidity);
|
|
564
|
-
|
|
565
|
-
if (maxAmmFill.gt(ZERO)) {
|
|
566
|
-
const baseFilled = BN.min(amount.sub(cumulativeBaseFilled), maxAmmFill);
|
|
567
|
-
const [afterSwapQuoteReserves, afterSwapBaseReserves] =
|
|
568
|
-
calculateAmmReservesAfterSwap(amm, 'base', baseFilled, swapDirection);
|
|
569
|
-
|
|
570
|
-
ammLiquidity = ammLiquidity.sub(baseFilled);
|
|
571
|
-
|
|
572
|
-
const quoteFilled = calculateQuoteAssetAmountSwapped(
|
|
573
|
-
amm.quoteAssetReserve.sub(afterSwapQuoteReserves).abs(),
|
|
574
|
-
amm.pegMultiplier,
|
|
575
|
-
swapDirection
|
|
576
|
-
);
|
|
577
|
-
|
|
578
|
-
cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled);
|
|
579
|
-
cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled);
|
|
580
|
-
|
|
581
|
-
amm.baseAssetReserve = afterSwapBaseReserves;
|
|
582
|
-
amm.quoteAssetReserve = afterSwapQuoteReserves;
|
|
583
|
-
|
|
584
|
-
worstPrice = calculatePrice(
|
|
585
|
-
amm.baseAssetReserve,
|
|
586
|
-
amm.quoteAssetReserve,
|
|
587
|
-
amm.pegMultiplier
|
|
588
|
-
);
|
|
589
|
-
|
|
590
|
-
if (cumulativeBaseFilled.eq(amount)) {
|
|
591
|
-
break;
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
if (!limitOrder) {
|
|
596
|
-
continue;
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
if (usersToSkip.has(limitOrder.userAccount)) {
|
|
600
|
-
continue;
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
const baseFilled = BN.min(
|
|
604
|
-
limitOrder.order.baseAssetAmount.sub(
|
|
605
|
-
limitOrder.order.baseAssetAmountFilled
|
|
606
|
-
),
|
|
607
|
-
amount.sub(cumulativeBaseFilled)
|
|
608
|
-
);
|
|
609
|
-
const quoteFilled = baseFilled.mul(limitOrderPrice).div(BASE_PRECISION);
|
|
610
|
-
|
|
611
|
-
cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled);
|
|
612
|
-
cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled);
|
|
613
|
-
|
|
614
|
-
worstPrice = limitOrderPrice;
|
|
615
|
-
|
|
616
|
-
if (cumulativeBaseFilled.eq(amount)) {
|
|
617
|
-
break;
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
limitOrder = limitOrders.next().value;
|
|
621
|
-
}
|
|
622
|
-
} else {
|
|
623
|
-
while (
|
|
624
|
-
!cumulativeQuoteFilled.eq(amount) &&
|
|
625
|
-
(ammLiquidity.gt(ZERO) || limitOrder)
|
|
626
|
-
) {
|
|
627
|
-
const limitOrderPrice = limitOrder?.getPrice(
|
|
628
|
-
mmOraclePriceData,
|
|
629
|
-
slot,
|
|
630
|
-
market.orderTickSize
|
|
631
|
-
);
|
|
632
|
-
|
|
633
|
-
let maxAmmFill: BN;
|
|
634
|
-
if (limitOrderPrice) {
|
|
635
|
-
const newQuoteReserves = squareRootBN(
|
|
636
|
-
invariant
|
|
637
|
-
.mul(PEG_PRECISION)
|
|
638
|
-
.mul(limitOrderPrice)
|
|
639
|
-
.div(amm.pegMultiplier)
|
|
640
|
-
.div(PRICE_PRECISION)
|
|
641
|
-
);
|
|
642
|
-
|
|
643
|
-
// will be zero if the limit order price is better than the amm price
|
|
644
|
-
maxAmmFill = takerIsLong
|
|
645
|
-
? newQuoteReserves.sub(amm.quoteAssetReserve)
|
|
646
|
-
: amm.quoteAssetReserve.sub(newQuoteReserves);
|
|
647
|
-
} else {
|
|
648
|
-
maxAmmFill = amount.sub(cumulativeQuoteFilled);
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
maxAmmFill = BN.min(maxAmmFill, ammLiquidity);
|
|
652
|
-
|
|
653
|
-
if (maxAmmFill.gt(ZERO)) {
|
|
654
|
-
const quoteFilled = BN.min(
|
|
655
|
-
amount.sub(cumulativeQuoteFilled),
|
|
656
|
-
maxAmmFill
|
|
657
|
-
);
|
|
658
|
-
const [afterSwapQuoteReserves, afterSwapBaseReserves] =
|
|
659
|
-
calculateAmmReservesAfterSwap(
|
|
660
|
-
amm,
|
|
661
|
-
'quote',
|
|
662
|
-
quoteFilled,
|
|
663
|
-
swapDirection
|
|
664
|
-
);
|
|
665
|
-
|
|
666
|
-
ammLiquidity = ammLiquidity.sub(quoteFilled);
|
|
667
|
-
|
|
668
|
-
const baseFilled = afterSwapBaseReserves
|
|
669
|
-
.sub(amm.baseAssetReserve)
|
|
670
|
-
.abs();
|
|
671
|
-
|
|
672
|
-
cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled);
|
|
673
|
-
cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled);
|
|
674
|
-
|
|
675
|
-
amm.baseAssetReserve = afterSwapBaseReserves;
|
|
676
|
-
amm.quoteAssetReserve = afterSwapQuoteReserves;
|
|
677
|
-
|
|
678
|
-
worstPrice = calculatePrice(
|
|
679
|
-
amm.baseAssetReserve,
|
|
680
|
-
amm.quoteAssetReserve,
|
|
681
|
-
amm.pegMultiplier
|
|
682
|
-
);
|
|
683
|
-
|
|
684
|
-
if (cumulativeQuoteFilled.eq(amount)) {
|
|
685
|
-
break;
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
if (!limitOrder) {
|
|
690
|
-
continue;
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
if (usersToSkip.has(limitOrder.userAccount)) {
|
|
694
|
-
continue;
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
const quoteFilled = BN.min(
|
|
698
|
-
limitOrder.order.baseAssetAmount
|
|
699
|
-
.sub(limitOrder.order.baseAssetAmountFilled)
|
|
700
|
-
.mul(limitOrderPrice)
|
|
701
|
-
.div(BASE_PRECISION),
|
|
702
|
-
amount.sub(cumulativeQuoteFilled)
|
|
703
|
-
);
|
|
704
|
-
|
|
705
|
-
const baseFilled = quoteFilled.mul(BASE_PRECISION).div(limitOrderPrice);
|
|
706
|
-
|
|
707
|
-
cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled);
|
|
708
|
-
cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled);
|
|
709
|
-
|
|
710
|
-
worstPrice = limitOrderPrice;
|
|
711
|
-
|
|
712
|
-
if (cumulativeQuoteFilled.eq(amount)) {
|
|
713
|
-
break;
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
limitOrder = limitOrders.next().value;
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
const entryPrice =
|
|
721
|
-
cumulativeBaseFilled && cumulativeBaseFilled.gt(ZERO)
|
|
722
|
-
? cumulativeQuoteFilled.mul(BASE_PRECISION).div(cumulativeBaseFilled)
|
|
723
|
-
: ZERO;
|
|
724
|
-
|
|
725
|
-
const priceImpact =
|
|
726
|
-
bestPrice && bestPrice.gt(ZERO)
|
|
727
|
-
? entryPrice.sub(bestPrice).mul(PRICE_PRECISION).div(bestPrice).abs()
|
|
728
|
-
: ZERO;
|
|
729
|
-
|
|
730
|
-
return {
|
|
731
|
-
entryPrice,
|
|
732
|
-
priceImpact,
|
|
733
|
-
bestPrice,
|
|
734
|
-
worstPrice,
|
|
735
|
-
baseFilled: cumulativeBaseFilled,
|
|
736
|
-
quoteFilled: cumulativeQuoteFilled,
|
|
737
|
-
};
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
/**
|
|
741
|
-
* Estimates entry price and price impact of a hypothetical taker order by walking a pre-built L2
|
|
742
|
-
* order book snapshot (asks for a long taker, bids for a short taker), rather than the live DLOB.
|
|
743
|
-
* Useful when an L2 snapshot is already available and a fresh DLOB walk isn't needed.
|
|
744
|
-
*
|
|
745
|
-
* @param {AssetType} assetType - Whether `amount` denominates base or quote
|
|
746
|
-
* @param {BN} amount - Order size, `basePrecision` for `'base'`; QUOTE_PRECISION (1e6) for `'quote'`
|
|
747
|
-
* @param {PositionDirection} direction - Taker's trade direction
|
|
748
|
-
* @param {BN} basePrecision - The base precision to use for size/price math (e.g. `BASE_PRECISION`)
|
|
749
|
-
* @param {L2OrderBook} l2 - Pre-computed L2 order book (bids/asks with price + size levels)
|
|
750
|
-
* @return {{ entryPrice: BN; priceImpact: BN; bestPrice: BN; worstPrice: BN; baseFilled: BN;
|
|
751
|
-
* quoteFilled: BN }} `entryPrice`/`bestPrice`/`worstPrice` are PRICE_PRECISION (1e6);
|
|
752
|
-
* `priceImpact` is `|entryPrice - bestPrice| / bestPrice` scaled by PRICE_PRECISION (1e6);
|
|
753
|
-
* `baseFilled` is `basePrecision`-scaled; `quoteFilled` is QUOTE_PRECISION (1e6). If the book
|
|
754
|
-
* is empty, `bestPrice`/`worstPrice` are `BN_MAX` (long) or `ZERO` (short) and `entryPrice`/
|
|
755
|
-
* `priceImpact` are `ZERO`
|
|
756
|
-
*/
|
|
757
|
-
export function calculateEstimatedEntryPriceWithL2(
|
|
758
|
-
assetType: AssetType,
|
|
759
|
-
amount: BN,
|
|
760
|
-
direction: PositionDirection,
|
|
761
|
-
basePrecision: BN,
|
|
762
|
-
l2: L2OrderBook
|
|
763
|
-
): {
|
|
764
|
-
entryPrice: BN;
|
|
765
|
-
priceImpact: BN;
|
|
766
|
-
bestPrice: BN;
|
|
767
|
-
worstPrice: BN;
|
|
768
|
-
baseFilled: BN;
|
|
769
|
-
quoteFilled: BN;
|
|
770
|
-
} {
|
|
771
|
-
const takerIsLong = isVariant(direction, 'long');
|
|
772
|
-
|
|
773
|
-
let cumulativeBaseFilled = ZERO;
|
|
774
|
-
let cumulativeQuoteFilled = ZERO;
|
|
775
|
-
|
|
776
|
-
const levels = [...(takerIsLong ? l2.asks : l2.bids)];
|
|
777
|
-
let nextLevel = levels.shift();
|
|
778
|
-
|
|
779
|
-
let bestPrice: BN;
|
|
780
|
-
let worstPrice: BN;
|
|
781
|
-
if (nextLevel) {
|
|
782
|
-
bestPrice = nextLevel.price;
|
|
783
|
-
worstPrice = nextLevel.price;
|
|
784
|
-
} else {
|
|
785
|
-
bestPrice = takerIsLong ? BN_MAX : ZERO;
|
|
786
|
-
worstPrice = bestPrice;
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
if (assetType === 'base') {
|
|
790
|
-
while (!cumulativeBaseFilled.eq(amount) && nextLevel) {
|
|
791
|
-
const price = nextLevel.price;
|
|
792
|
-
const size = nextLevel.size;
|
|
793
|
-
|
|
794
|
-
worstPrice = price;
|
|
795
|
-
|
|
796
|
-
const baseFilled = BN.min(size, amount.sub(cumulativeBaseFilled));
|
|
797
|
-
const quoteFilled = baseFilled.mul(price).div(basePrecision);
|
|
798
|
-
|
|
799
|
-
cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled);
|
|
800
|
-
cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled);
|
|
801
|
-
|
|
802
|
-
nextLevel = levels.shift();
|
|
803
|
-
}
|
|
804
|
-
} else {
|
|
805
|
-
while (!cumulativeQuoteFilled.eq(amount) && nextLevel) {
|
|
806
|
-
const price = nextLevel.price;
|
|
807
|
-
const size = nextLevel.size;
|
|
808
|
-
|
|
809
|
-
worstPrice = price;
|
|
810
|
-
|
|
811
|
-
const quoteFilled = BN.min(
|
|
812
|
-
size.mul(price).div(basePrecision),
|
|
813
|
-
amount.sub(cumulativeQuoteFilled)
|
|
814
|
-
);
|
|
815
|
-
const baseFilled = quoteFilled.mul(basePrecision).div(price);
|
|
816
|
-
|
|
817
|
-
cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled);
|
|
818
|
-
cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled);
|
|
819
|
-
|
|
820
|
-
nextLevel = levels.shift();
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
|
|
824
|
-
const entryPrice =
|
|
825
|
-
cumulativeBaseFilled && cumulativeBaseFilled.gt(ZERO)
|
|
826
|
-
? cumulativeQuoteFilled.mul(basePrecision).div(cumulativeBaseFilled)
|
|
827
|
-
: ZERO;
|
|
828
|
-
|
|
829
|
-
const priceImpact =
|
|
830
|
-
bestPrice && bestPrice.gt(ZERO)
|
|
831
|
-
? entryPrice.sub(bestPrice).mul(PRICE_PRECISION).div(bestPrice).abs()
|
|
832
|
-
: ZERO;
|
|
833
|
-
|
|
834
|
-
return {
|
|
835
|
-
entryPrice,
|
|
836
|
-
priceImpact,
|
|
837
|
-
bestPrice,
|
|
838
|
-
worstPrice,
|
|
839
|
-
baseFilled: cumulativeBaseFilled,
|
|
840
|
-
quoteFilled: cumulativeQuoteFilled,
|
|
841
|
-
};
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
/**
|
|
845
|
-
* Estimates a user's trailing-30-day taker + maker volume as of `now`, using the same
|
|
846
|
-
* time-weighted decay shape as the on-chain `update_taker_volume_30d` / `update_maker_volume_30d`
|
|
847
|
-
* (`calculate_rolling_sum`) but without requiring a new fill to trigger the on-chain update —
|
|
848
|
-
* useful for e.g. displaying live fee-tier progress between actual `UserStats` refreshes.
|
|
849
|
-
*
|
|
850
|
-
* @param {UserStatsAccount} userStatsAccount - The user's stats account (`takerVolume30D`,
|
|
851
|
-
* `makerVolume30D`, and their respective last-update timestamps)
|
|
852
|
-
* @param {BN} [now] - Current unix timestamp (seconds); defaults to `Date.now() / 1000`
|
|
853
|
-
* @return {BN} Estimated combined 30-day taker + maker volume, QUOTE_PRECISION (1e6)
|
|
854
|
-
*/
|
|
855
|
-
export function getUser30dRollingVolumeEstimate(
|
|
856
|
-
userStatsAccount: UserStatsAccount,
|
|
857
|
-
now?: BN
|
|
858
|
-
) {
|
|
859
|
-
now = now || new BN(new Date().getTime() / 1000);
|
|
860
|
-
const sinceLastTaker = BN.max(
|
|
861
|
-
now.sub(userStatsAccount.lastTakerVolume30DTs),
|
|
862
|
-
ZERO
|
|
863
|
-
);
|
|
864
|
-
const sinceLastMaker = BN.max(
|
|
865
|
-
now.sub(userStatsAccount.lastMakerVolume30DTs),
|
|
866
|
-
ZERO
|
|
867
|
-
);
|
|
868
|
-
const thirtyDaysInSeconds = new BN(60 * 60 * 24 * 30);
|
|
869
|
-
const last30dVolume = userStatsAccount.takerVolume30D
|
|
870
|
-
.mul(BN.max(thirtyDaysInSeconds.sub(sinceLastTaker), ZERO))
|
|
871
|
-
.div(thirtyDaysInSeconds)
|
|
872
|
-
.add(
|
|
873
|
-
userStatsAccount.makerVolume30D
|
|
874
|
-
.mul(BN.max(thirtyDaysInSeconds.sub(sinceLastMaker), ZERO))
|
|
875
|
-
.div(thirtyDaysInSeconds)
|
|
876
|
-
);
|
|
877
|
-
|
|
878
|
-
return last30dVolume;
|
|
879
|
-
}
|