UNPKG

@drift-labs/sdk-browser

Version:
255 lines (254 loc) 11.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.calculateOrderBaseAssetAmount = exports.isSignedMsgOrder = exports.isTakingOrder = exports.isRestingLimitOrder = exports.isTriggered = exports.mustBeTriggered = exports.isLimitOrder = exports.isMarketOrder = exports.isOrderExpired = exports.calculateBaseAssetAmountToFillUpToLimitPrice = exports.calculateBaseAssetAmountForAmmToFulfill = exports.isFillableByVAMM = exports.hasAuctionPrice = exports.hasLimitPrice = exports.applyProtectedMakerParams = exports.getLimitPrice = exports.standardizePrice = exports.standardizeBaseAssetAmount = exports.isOrderReduceOnly = exports.isOrderRiskIncreasingInSameDirection = exports.isOrderRiskIncreasing = void 0; const types_1 = require("../types"); const numericConstants_1 = require("../constants/numericConstants"); const anchor_1 = require("@coral-xyz/anchor"); const auction_1 = require("./auction"); const amm_1 = require("./amm"); function isOrderRiskIncreasing(user, order) { if (!(0, types_1.isVariant)(order.status, 'open')) { return false; } const position = user.getPerpPosition(order.marketIndex) || user.getEmptyPosition(order.marketIndex); // if no position exists, it's risk increasing if (position.baseAssetAmount.eq(numericConstants_1.ZERO)) { return true; } // if position is long and order is long if (position.baseAssetAmount.gt(numericConstants_1.ZERO) && (0, types_1.isVariant)(order.direction, 'long')) { return true; } // if position is short and order is short if (position.baseAssetAmount.lt(numericConstants_1.ZERO) && (0, types_1.isVariant)(order.direction, 'short')) { return true; } const baseAssetAmountToFill = order.baseAssetAmount.sub(order.baseAssetAmountFilled); // if order will flip position if (baseAssetAmountToFill.gt(position.baseAssetAmount.abs().mul(numericConstants_1.TWO))) { return true; } return false; } exports.isOrderRiskIncreasing = isOrderRiskIncreasing; function isOrderRiskIncreasingInSameDirection(user, order) { if (!(0, types_1.isVariant)(order.status, 'open')) { return false; } const position = user.getPerpPosition(order.marketIndex) || user.getEmptyPosition(order.marketIndex); // if no position exists, it's risk increasing if (position.baseAssetAmount.eq(numericConstants_1.ZERO)) { return true; } // if position is long and order is long if (position.baseAssetAmount.gt(numericConstants_1.ZERO) && (0, types_1.isVariant)(order.direction, 'long')) { return true; } // if position is short and order is short if (position.baseAssetAmount.lt(numericConstants_1.ZERO) && (0, types_1.isVariant)(order.direction, 'short')) { return true; } return false; } exports.isOrderRiskIncreasingInSameDirection = isOrderRiskIncreasingInSameDirection; function isOrderReduceOnly(user, order) { if (!(0, types_1.isVariant)(order.status, 'open')) { return false; } const position = user.getPerpPosition(order.marketIndex) || user.getEmptyPosition(order.marketIndex); // if position is long and order is long if (position.baseAssetAmount.gte(numericConstants_1.ZERO) && (0, types_1.isVariant)(order.direction, 'long')) { return false; } // if position is short and order is short if (position.baseAssetAmount.lte(numericConstants_1.ZERO) && (0, types_1.isVariant)(order.direction, 'short')) { return false; } return true; } exports.isOrderReduceOnly = isOrderReduceOnly; function standardizeBaseAssetAmount(baseAssetAmount, stepSize) { const remainder = baseAssetAmount.mod(stepSize); return baseAssetAmount.sub(remainder); } exports.standardizeBaseAssetAmount = standardizeBaseAssetAmount; function standardizePrice(price, tickSize, direction) { if (price.eq(numericConstants_1.ZERO)) { console.log('price is zero'); return price; } const remainder = price.mod(tickSize); if (remainder.eq(numericConstants_1.ZERO)) { return price; } if ((0, types_1.isVariant)(direction, 'long')) { return price.sub(remainder); } else { return price.add(tickSize).sub(remainder); } } exports.standardizePrice = standardizePrice; function getLimitPrice(order, oraclePriceData, slot, fallbackPrice, protectedMakerParams) { let limitPrice; if (hasAuctionPrice(order, slot)) { limitPrice = (0, auction_1.getAuctionPrice)(order, slot, oraclePriceData.price); } else if (order.oraclePriceOffset !== 0) { limitPrice = anchor_1.BN.max(oraclePriceData.price.add(new anchor_1.BN(order.oraclePriceOffset)), numericConstants_1.ONE); } else if (order.price.eq(numericConstants_1.ZERO)) { limitPrice = fallbackPrice; } else { limitPrice = order.price; } if (protectedMakerParams) { limitPrice = applyProtectedMakerParams(limitPrice, order.direction, protectedMakerParams); } return limitPrice; } exports.getLimitPrice = getLimitPrice; function applyProtectedMakerParams(limitPrice, direction, protectedMakerParams) { const minOffset = protectedMakerParams.tickSize.muln(8); let limitPriceBpsDivisor; if (protectedMakerParams.limitPriceDivisor > 0) { limitPriceBpsDivisor = 10000 / protectedMakerParams.limitPriceDivisor; } else { limitPriceBpsDivisor = 1000; } const limitPriceOffset = anchor_1.BN.min(anchor_1.BN.max(anchor_1.BN.max(limitPrice.divn(limitPriceBpsDivisor), minOffset), protectedMakerParams.dynamicOffset), limitPrice.divn(20)); if ((0, types_1.isVariant)(direction, 'long')) { return anchor_1.BN.max(limitPrice.sub(limitPriceOffset), protectedMakerParams.tickSize); } else { return limitPrice.add(limitPriceOffset); } } exports.applyProtectedMakerParams = applyProtectedMakerParams; function hasLimitPrice(order, slot) { return (order.price.gt(numericConstants_1.ZERO) || order.oraclePriceOffset != 0 || !(0, auction_1.isAuctionComplete)(order, slot)); } exports.hasLimitPrice = hasLimitPrice; function hasAuctionPrice(order, slot) { return (!(0, auction_1.isAuctionComplete)(order, slot) && (!order.auctionStartPrice.eq(numericConstants_1.ZERO) || !order.auctionEndPrice.eq(numericConstants_1.ZERO))); } exports.hasAuctionPrice = hasAuctionPrice; function isFillableByVAMM(order, market, mmOraclePriceData, slot, ts, minAuctionDuration) { return (((0, auction_1.isFallbackAvailableLiquiditySource)(order, minAuctionDuration, slot) && calculateBaseAssetAmountForAmmToFulfill(order, market, mmOraclePriceData, slot).gt(numericConstants_1.ZERO)) || isOrderExpired(order, ts)); } exports.isFillableByVAMM = isFillableByVAMM; function calculateBaseAssetAmountForAmmToFulfill(order, market, mmOraclePriceData, slot) { if (mustBeTriggered(order) && !isTriggered(order)) { return numericConstants_1.ZERO; } const limitPrice = getLimitPrice(order, mmOraclePriceData, slot); let baseAssetAmount; const updatedAMM = (0, amm_1.calculateUpdatedAMM)(market.amm, mmOraclePriceData); if (limitPrice !== undefined) { baseAssetAmount = calculateBaseAssetAmountToFillUpToLimitPrice(order, updatedAMM, limitPrice, mmOraclePriceData); } else { baseAssetAmount = order.baseAssetAmount.sub(order.baseAssetAmountFilled); } const maxBaseAssetAmount = (0, amm_1.calculateMaxBaseAssetAmountFillable)(updatedAMM, order.direction); return anchor_1.BN.min(maxBaseAssetAmount, baseAssetAmount); } exports.calculateBaseAssetAmountForAmmToFulfill = calculateBaseAssetAmountForAmmToFulfill; function calculateBaseAssetAmountToFillUpToLimitPrice(order, amm, limitPrice, mmOraclePriceData) { const adjustedLimitPrice = (0, types_1.isVariant)(order.direction, 'long') ? limitPrice.sub(amm.orderTickSize) : limitPrice.add(amm.orderTickSize); const [maxAmountToTrade, direction] = (0, amm_1.calculateMaxBaseAssetAmountToTrade)(amm, adjustedLimitPrice, order.direction, mmOraclePriceData); const baseAssetAmount = standardizeBaseAssetAmount(maxAmountToTrade, amm.orderStepSize); // Check that directions are the same const sameDirection = isSameDirection(direction, order.direction); if (!sameDirection) { return numericConstants_1.ZERO; } const baseAssetAmountUnfilled = order.baseAssetAmount.sub(order.baseAssetAmountFilled); return baseAssetAmount.gt(baseAssetAmountUnfilled) ? baseAssetAmountUnfilled : baseAssetAmount; } exports.calculateBaseAssetAmountToFillUpToLimitPrice = calculateBaseAssetAmountToFillUpToLimitPrice; function isSameDirection(firstDirection, secondDirection) { return (((0, types_1.isVariant)(firstDirection, 'long') && (0, types_1.isVariant)(secondDirection, 'long')) || ((0, types_1.isVariant)(firstDirection, 'short') && (0, types_1.isVariant)(secondDirection, 'short'))); } function isOrderExpired(order, ts, enforceBuffer = false, bufferSeconds = 15) { if (mustBeTriggered(order) || !(0, types_1.isVariant)(order.status, 'open') || order.maxTs.eq(numericConstants_1.ZERO)) { return false; } let maxTs; if (enforceBuffer && isLimitOrder(order)) { maxTs = order.maxTs.addn(bufferSeconds); } else { maxTs = order.maxTs; } return new anchor_1.BN(ts).gt(maxTs); } exports.isOrderExpired = isOrderExpired; function isMarketOrder(order) { return (0, types_1.isOneOfVariant)(order.orderType, ['market', 'triggerMarket', 'oracle']); } exports.isMarketOrder = isMarketOrder; function isLimitOrder(order) { return (0, types_1.isOneOfVariant)(order.orderType, ['limit', 'triggerLimit']); } exports.isLimitOrder = isLimitOrder; function mustBeTriggered(order) { return (0, types_1.isOneOfVariant)(order.orderType, ['triggerMarket', 'triggerLimit']); } exports.mustBeTriggered = mustBeTriggered; function isTriggered(order) { return (0, types_1.isOneOfVariant)(order.triggerCondition, [ 'triggeredAbove', 'triggeredBelow', ]); } exports.isTriggered = isTriggered; function isRestingLimitOrder(order, slot) { if (!isLimitOrder(order)) { return false; } return order.postOnly || (0, auction_1.isAuctionComplete)(order, slot); } exports.isRestingLimitOrder = isRestingLimitOrder; function isTakingOrder(order, slot) { return isMarketOrder(order) || !isRestingLimitOrder(order, slot); } exports.isTakingOrder = isTakingOrder; const FLAG_IS_SIGNED_MSG = 0x01; function isSignedMsgOrder(order) { return (order.bitFlags & FLAG_IS_SIGNED_MSG) !== 0; } exports.isSignedMsgOrder = isSignedMsgOrder; function calculateOrderBaseAssetAmount(order, existingBaseAssetAmount) { if (!order.reduceOnly) { return order.baseAssetAmount; } if ((0, types_1.isVariant)(order.direction, 'long')) { return anchor_1.BN.min(anchor_1.BN.min(existingBaseAssetAmount, numericConstants_1.ZERO).abs(), order.baseAssetAmount); } else { return anchor_1.BN.min(anchor_1.BN.max(existingBaseAssetAmount, numericConstants_1.ZERO), order.baseAssetAmount); } } exports.calculateOrderBaseAssetAmount = calculateOrderBaseAssetAmount;