import * as anchor from '@project-serum/anchor' ; import { Program } from '@project-serum/anchor' ; import { Keypair } from '@solana/web3.js' ; import BN from 'bn.js' ; import { Admin , MARK_PRICE_PRECISION , calculateMarkPrice , calculateTradeSlippage , calculateTargetPriceTrade , PositionDirection , PEG_PRECISION , MAX_LEVERAGE , QUOTE_PRECISION , convertToNumber , } from '../sdk/src' ; import { liquidityBook } from './liquidityBook' ; import { ClearingHouseUser } from '../sdk/src/clearingHouseUser' ; import { createPriceFeed , mockUSDCMint , mockUserUSDCAccount , } from './testHelpers' ; describe ( 'AMM Curve' , ( ) => { const MAX_DEPOSIT = 1000 ; const initialSOLPrice = 150 ; const MAX_USER_TRADE = MAX_DEPOSIT * MAX_LEVERAGE . toNumber ( ) ; const provider = anchor . Provider . local ( ) ; const connection = provider . connection ; anchor . setProvider ( provider ) ; const chProgram = anchor . workspace . ClearingHouse as Program ; const clearingHouse = Admin . from ( connection , provider . wallet , chProgram . programId ) ; const kSqrt = new anchor . BN ( 2 * 10 ** 12 ) ; let usdcMint ; let userUSDCAccount ; let solUsdOracle ; const marketIndex = new BN ( 0 ) ; const initialSOLPriceBN = new BN ( initialSOLPrice * PEG_PRECISION . toNumber ( ) ) ; function normAssetAmount ( assetAmount , pegMultiplier ) { return assetAmount . mul ( MARK_PRICE_PRECISION ) . div ( pegMultiplier ) ; } const usdcAmount = new BN ( 1000 * 10 ** 6 ) ; const solPositionInitialValue = usdcAmount ; let userAccount ; before ( async ( ) => { usdcMint = await mockUSDCMint ( provider ) ; userUSDCAccount = await mockUserUSDCAccount ( usdcMint , usdcAmount , provider ) ; await clearingHouse . initialize ( usdcMint . publicKey , true ) ; await clearingHouse . subscribe ( ) ; solUsdOracle = await createPriceFeed ( { oracleProgram : anchor . workspace . Pyth , initPrice : initialSOLPrice , } ) ; const periodicity = new BN ( 60 * 60 ) ; const kSqrtNorm = normAssetAmount ( kSqrt , initialSOLPriceBN ) ; await clearingHouse . initializeMarket ( marketIndex , solUsdOracle , kSqrtNorm , kSqrtNorm , periodicity , initialSOLPriceBN ) ; await clearingHouse . initializeUserAccount ( ) ; userAccount = ClearingHouseUser . from ( clearingHouse , provider . wallet . publicKey ) ; await userAccount . subscribe ( ) ; } ) ; after ( async ( ) => { await clearingHouse . unsubscribe ( ) ; await userAccount . unsubscribe ( ) ; } ) ; const showBook = ( marketIndex ) => { const market = clearingHouse . getMarketsAccount ( ) . markets [ marketIndex . toNumber ( ) ] ; const currentMark = calculateMarkPrice ( market ) ; const [ bidsPrice , bidsCumSize , asksPrice , asksCumSize ] = liquidityBook ( market , 3 , 0.5 ) ; for ( let i = asksCumSize . length - 1 ; i >= 0 ; i -- ) { console . log ( convertToNumber ( asksPrice [ i ] ) , convertToNumber ( asksCumSize [ i ] , QUOTE_PRECISION ) ) ; } console . log ( '------------' ) ; console . log ( currentMark . toNumber ( ) / MARK_PRICE_PRECISION . toNumber ( ) ) ; console . log ( 'peg:' , convertToNumber ( market . amm . pegMultiplier , PEG_PRECISION ) , 'k (M*M):' , convertToNumber ( market . amm . sqrtK ) ) ; console . log ( '------------' ) ; for ( let i = 0 ; i < bidsCumSize . length ; i ++ ) { console . log ( convertToNumber ( bidsPrice [ i ] ) , convertToNumber ( bidsCumSize [ i ] , QUOTE_PRECISION ) ) ; } } ; it ( 'After Deposit' , async ( ) => { await clearingHouse . depositCollateral ( usdcAmount , userUSDCAccount . publicKey ) ; } ) ; it ( 'After Position Taken' , async ( ) => { await clearingHouse . openPosition ( PositionDirection . LONG , solPositionInitialValue , marketIndex ) ; const avgSlippageCenter = calculateTradeSlippage ( PositionDirection . LONG , new BN ( MAX_USER_TRADE * MARK_PRICE_PRECISION . toNumber ( ) ) , clearingHouse . getMarket ( 0 ) ) [ 0 ] ; showBook ( marketIndex ) ; const targetPriceUp = new BN ( initialSOLPrice * MARK_PRICE_PRECISION . toNumber ( ) * 2 ) ; const [ _direction , tradeSize , _ ] = calculateTargetPriceTrade ( clearingHouse . getMarket ( marketIndex ) , targetPriceUp ) ; await clearingHouse . moveAmmToPrice ( marketIndex , targetPriceUp ) ; const avgSlippage25PctOut = calculateTradeSlippage ( PositionDirection . LONG , new BN ( MAX_USER_TRADE * MARK_PRICE_PRECISION . toNumber ( ) ) , clearingHouse . getMarket ( 0 ) ) [ 0 ] ; showBook ( marketIndex ) ; console . log ( 'arbBot Long Size' , convertToNumber ( tradeSize , QUOTE_PRECISION ) , '\n Center Slippage:' , convertToNumber ( avgSlippageCenter ) / 100 , '\n 100% up out Slippage:' , convertToNumber ( avgSlippage25PctOut ) / 100 ) ; } ) ; } ) ;