import { Market , PositionDirection } from '../types' ; import { BN } from '@project-serum/anchor' ; import { assert } from '../assert/assert' ; import { MARK_PRICE_PRECISION , PEG_PRECISION , AMM_TO_QUOTE_PRECISION_RATIO , ZERO , } from '../constants/numericConstants' ; import { calculateMarkPrice } from './market' ; import { calculateAmmReservesAfterSwap , calculatePrice , getSwapDirection , AssetType , } from './amm' ; import { squareRootBN } from './utils' ; const MAXPCT = new BN ( 1000 ) ; export type PriceImpactUnit = 'entryPrice' | 'maxPrice' | 'priceDelta' | 'priceDeltaAsNumber' | 'pctAvg' | 'pctMax' | 'quoteAssetAmount' | 'quoteAssetAmountPeg' | 'acquiredBaseAssetAmount' | 'acquiredQuoteAssetAmount' | 'all' ; param direction param amount param market return export function calculateTradeSlippage ( direction , amount , market , inputAssetType = 'quote' ) { const oldPrice = calculateMarkPrice ( market ) ; if ( amount . eq ( ZERO ) ) { return [ ZERO , ZERO , oldPrice , oldPrice ] ; } const [ acquiredBase , acquiredQuote ] = calculateTradeAcquiredAmounts ( direction , amount , market , inputAssetType ) ; const entryPrice = calculatePrice ( acquiredBase , acquiredQuote , market . amm . pegMultiplier ) . mul ( new BN ( - 1 ) ) ; const newPrice = calculatePrice ( market . amm . baseAssetReserve . sub ( acquiredBase ) , market . amm . quoteAssetReserve . sub ( acquiredQuote ) , market . amm . pegMultiplier ) ; if ( direction == PositionDirection . SHORT ) { assert ( newPrice . lt ( oldPrice ) ) ; } else { assert ( oldPrice . lt ( newPrice ) ) ; } const pctMaxSlippage = newPrice . sub ( oldPrice ) . mul ( MARK_PRICE_PRECISION ) . div ( oldPrice ) . abs ( ) ; const pctAvgSlippage = entryPrice . sub ( oldPrice ) . mul ( MARK_PRICE_PRECISION ) . div ( oldPrice ) . abs ( ) ; return [ pctAvgSlippage , pctMaxSlippage , entryPrice , newPrice ] ; } param direction param amount param market return export function calculateTradeAcquiredAmounts ( direction , amount , market , inputAssetType = 'quote' ) { if ( amount . eq ( ZERO ) ) { return [ ZERO , ZERO ] ; } const [ newQuoteAssetReserve , newBaseAssetReserve ] = calculateAmmReservesAfterSwap ( market . amm , inputAssetType , amount , getSwapDirection ( inputAssetType , direction ) ) ; const acquiredBase = market . amm . baseAssetReserve . sub ( newBaseAssetReserve ) ; const acquiredQuote = market . amm . quoteAssetReserve . sub ( newQuoteAssetReserve ) ; return [ acquiredBase , acquiredQuote ] ; } param market param targetPrice param pct returns export function calculateTargetPriceTrade ( market , targetPrice , pct = MAXPCT , outputAssetType = 'quote' ) { assert ( market . amm . baseAssetReserve . gt ( ZERO ) ) ; assert ( targetPrice . gt ( ZERO ) ) ; assert ( pct . lte ( MAXPCT ) && pct . gt ( ZERO ) ) ; const markPriceBefore = calculateMarkPrice ( market ) ; if ( targetPrice . gt ( markPriceBefore ) ) { const priceGap = targetPrice . sub ( markPriceBefore ) ; const priceGapScaled = priceGap . mul ( pct ) . div ( MAXPCT ) ; targetPrice = markPriceBefore . add ( priceGapScaled ) ; } else { const priceGap = markPriceBefore . sub ( targetPrice ) ; const priceGapScaled = priceGap . mul ( pct ) . div ( MAXPCT ) ; targetPrice = markPriceBefore . sub ( priceGapScaled ) ; } let direction ; let tradeSize ; let baseSize ; const baseAssetReserveBefore = market . amm . baseAssetReserve ; const quoteAssetReserveBefore = market . amm . quoteAssetReserve ; const peg = market . amm . pegMultiplier ; const invariant = market . amm . sqrtK . mul ( market . amm . sqrtK ) ; const k = invariant . mul ( MARK_PRICE_PRECISION ) ; let baseAssetReserveAfter ; let quoteAssetReserveAfter ; const biasModifier = new BN ( 1 ) ; let markPriceAfter ; if ( markPriceBefore . gt ( targetPrice ) ) { baseAssetReserveAfter = squareRootBN ( k . div ( targetPrice ) . mul ( peg ) . div ( PEG_PRECISION ) . sub ( biasModifier ) ) . sub ( new BN ( 1 ) ) ; quoteAssetReserveAfter = k . div ( MARK_PRICE_PRECISION ) . div ( baseAssetReserveAfter ) ; markPriceAfter = calculatePrice ( baseAssetReserveAfter , quoteAssetReserveAfter , peg ) ; direction = PositionDirection . SHORT ; tradeSize = quoteAssetReserveBefore . sub ( quoteAssetReserveAfter ) . mul ( peg ) . div ( PEG_PRECISION ) . div ( AMM_TO_QUOTE_PRECISION_RATIO ) ; baseSize = baseAssetReserveAfter . sub ( baseAssetReserveBefore ) ; } else if ( markPriceBefore . lt ( targetPrice ) ) { baseAssetReserveAfter = squareRootBN ( k . div ( targetPrice ) . mul ( peg ) . div ( PEG_PRECISION ) . add ( biasModifier ) ) . add ( new BN ( 1 ) ) ; quoteAssetReserveAfter = k . div ( MARK_PRICE_PRECISION ) . div ( baseAssetReserveAfter ) ; markPriceAfter = calculatePrice ( baseAssetReserveAfter , quoteAssetReserveAfter , peg ) ; direction = PositionDirection . LONG ; tradeSize = quoteAssetReserveAfter . sub ( quoteAssetReserveBefore ) . mul ( peg ) . div ( PEG_PRECISION ) . div ( AMM_TO_QUOTE_PRECISION_RATIO ) ; baseSize = baseAssetReserveBefore . sub ( baseAssetReserveAfter ) ; } else { direction = PositionDirection . LONG ; tradeSize = ZERO ; return [ direction , tradeSize , targetPrice , targetPrice ] ; } let tp1 = targetPrice ; let tp2 = markPriceAfter ; let originalDiff = targetPrice . sub ( markPriceBefore ) ; if ( direction == PositionDirection . SHORT ) { tp1 = markPriceAfter ; tp2 = targetPrice ; originalDiff = markPriceBefore . sub ( targetPrice ) ; } const entryPrice = tradeSize . mul ( AMM_TO_QUOTE_PRECISION_RATIO ) . mul ( MARK_PRICE_PRECISION ) . div ( baseSize . abs ( ) ) ; assert ( tp1 . sub ( tp2 ) . lte ( originalDiff ) , 'Target Price Calculation incorrect' ) ; assert ( tp2 . lte ( tp1 ) || tp2 . sub ( tp1 ) . abs ( ) < 100000 , 'Target Price Calculation incorrect' + tp2 . toString ( ) + '>=' + tp1 . toString ( ) + 'err: ' + tp2 . sub ( tp1 ) . abs ( ) . toString ( ) ) ; if ( outputAssetType == 'quote' ) { return [ direction , tradeSize , entryPrice , targetPrice ] ; } else { return [ direction , baseSize , entryPrice , targetPrice ] ; } }