@drift-labs/sdk
Advanced tools
Comparing version 2.12.0-beta.0 to 2.12.0-beta.1
@@ -14,6 +14,6 @@ "use strict"; | ||
exports.getTokenAddress = getTokenAddress; | ||
const cluster = 'devnet'; | ||
const env = 'devnet'; | ||
const main = async () => { | ||
// Initialize Drift SDK | ||
const sdkConfig = (0, __2.initialize)({ env: cluster }); | ||
const sdkConfig = (0, __2.initialize)({ env }); | ||
// Set up the Wallet and Provider | ||
@@ -40,3 +40,3 @@ const privateKey = process.env.BOT_PRIVATE_KEY; // stored as an array string | ||
programID: driftPublicKey, | ||
...(0, __2.getMarketsAndOraclesForSubscription)(cluster), | ||
...(0, __2.getMarketsAndOraclesForSubscription)(env), | ||
accountSubscription: { | ||
@@ -48,3 +48,3 @@ type: 'polling', | ||
await driftClient.subscribe(); | ||
// Set up Clearing House user client | ||
// Set up user client | ||
const user = new __2.User({ | ||
@@ -58,3 +58,3 @@ driftClient: driftClient, | ||
}); | ||
//// Check if clearing house account exists for the current wallet | ||
//// Check if user account exists for the current wallet | ||
const userAccountExists = await user.exists(); | ||
@@ -69,19 +69,18 @@ if (!userAccountExists) { | ||
const solMarketInfo = sdkConfig.PERP_MARKETS.find((market) => market.baseAssetSymbol === 'SOL'); | ||
const currentMarketPrice = (0, __2.calculateReservePrice)(driftClient.getPerpMarketAccount(solMarketInfo.marketIndex), undefined); | ||
const formattedPrice = (0, __2.convertToNumber)(currentMarketPrice, __2.PRICE_PRECISION); | ||
console.log(`Current Market Price is $${formattedPrice}`); | ||
const marketIndex = solMarketInfo.marketIndex; | ||
const [bid, ask] = (0, __1.calculateBidAskPrice)(driftClient.getPerpMarketAccount(marketIndex).amm, driftClient.getOracleDataForPerpMarket(marketIndex)); | ||
const formattedBidPrice = (0, __2.convertToNumber)(bid, __2.PRICE_PRECISION); | ||
const formattedAskPrice = (0, __2.convertToNumber)(ask, __2.PRICE_PRECISION); | ||
console.log(`Current amm bid and ask price are $${formattedBidPrice} and $${formattedAskPrice}`); | ||
// Estimate the slippage for a $5000 LONG trade | ||
const solMarketAccount = driftClient.getPerpMarketAccount(solMarketInfo.marketIndex); | ||
const longAmount = new anchor_1.BN(5000).mul(__2.QUOTE_PRECISION); | ||
const slippage = (0, __2.convertToNumber)((0, __2.calculateTradeSlippage)(__2.PositionDirection.LONG, longAmount, solMarketAccount, 'quote', driftClient.getOracleDataForPerpMarket(solMarketInfo.marketIndex))[0], __2.PRICE_PRECISION); | ||
console.log(`Slippage for a $5000 LONG on the SOL market would be $${slippage}`); | ||
// Make a $5000 LONG trade | ||
await driftClient.openPosition(__2.PositionDirection.LONG, longAmount, solMarketInfo.marketIndex); | ||
console.log(`LONGED $5000 SOL`); | ||
// Reduce the position by $2000 | ||
const reduceAmount = new anchor_1.BN(2000).mul(__2.QUOTE_PRECISION); | ||
await driftClient.openPosition(__2.PositionDirection.SHORT, reduceAmount, solMarketInfo.marketIndex); | ||
// Close the rest of the position | ||
await driftClient.closePosition(solMarketInfo.marketIndex); | ||
const slippage = (0, __2.convertToNumber)((0, __2.calculateTradeSlippage)(__2.PositionDirection.LONG, new anchor_1.BN(1).mul(__1.BASE_PRECISION), solMarketAccount, 'base', driftClient.getOracleDataForPerpMarket(solMarketInfo.marketIndex))[0], __2.PRICE_PRECISION); | ||
console.log(`Slippage for a 1 SOL-PERP would be $${slippage}`); | ||
await driftClient.placePerpOrder((0, __1.getMarketOrderParams)({ | ||
baseAssetAmount: new anchor_1.BN(1).mul(__1.BASE_PRECISION), | ||
direction: __2.PositionDirection.LONG, | ||
marketIndex: solMarketAccount.marketIndex, | ||
})); | ||
console.log(`Placed a 1 SOL-PERP LONG order`); | ||
}; | ||
main(); |
@@ -6,2 +6,3 @@ /// <reference types="bn.js" /> | ||
import { OraclePriceData } from '../oracles/types'; | ||
import { DLOB } from '../dlob/DLOB'; | ||
export declare type PriceImpactUnit = 'entryPrice' | 'maxPrice' | 'priceDelta' | 'priceDeltaAsNumber' | 'pctAvg' | 'pctMax' | 'quoteAssetAmount' | 'quoteAssetAmountPeg' | 'acquiredBaseAssetAmount' | 'acquiredQuoteAssetAmount' | 'all'; | ||
@@ -56,1 +57,15 @@ /** | ||
export declare function calculateTargetPriceTrade(market: PerpMarketAccount, targetPrice: BN, pct?: BN, outputAssetType?: AssetType, oraclePriceData?: OraclePriceData, useSpread?: boolean): [PositionDirection, BN, BN, BN]; | ||
/** | ||
* Calculates the estimated entry price and price impact of order, in base or quote | ||
* Price impact is based on the difference between the entry price and the best bid/ask price (whether it's dlob or vamm) | ||
* | ||
* @param assetType | ||
* @param amount | ||
* @param direction | ||
* @param market | ||
* @param oraclePriceData | ||
* @param dlob | ||
* @param slot | ||
* @param minPerpAuctionDuration | ||
*/ | ||
export declare function calculateEstimatedPerpEntryPrice(assetType: AssetType, amount: BN, direction: PositionDirection, market: PerpMarketAccount, oraclePriceData: OraclePriceData, dlob: DLOB, slot: number, minPerpAuctionDuration: number): [BN, BN]; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.calculateTargetPriceTrade = exports.calculateTradeAcquiredAmounts = exports.calculateTradeSlippage = void 0; | ||
exports.calculateEstimatedPerpEntryPrice = exports.calculateTargetPriceTrade = exports.calculateTradeAcquiredAmounts = exports.calculateTradeSlippage = void 0; | ||
const types_1 = require("../types"); | ||
@@ -250,1 +250,135 @@ const anchor_1 = require("@project-serum/anchor"); | ||
exports.calculateTargetPriceTrade = calculateTargetPriceTrade; | ||
/** | ||
* Calculates the estimated entry price and price impact of order, in base or quote | ||
* Price impact is based on the difference between the entry price and the best bid/ask price (whether it's dlob or vamm) | ||
* | ||
* @param assetType | ||
* @param amount | ||
* @param direction | ||
* @param market | ||
* @param oraclePriceData | ||
* @param dlob | ||
* @param slot | ||
* @param minPerpAuctionDuration | ||
*/ | ||
function calculateEstimatedPerpEntryPrice(assetType, amount, direction, market, oraclePriceData, dlob, slot, minPerpAuctionDuration) { | ||
if (amount.eq(numericConstants_1.ZERO)) { | ||
return [numericConstants_1.ZERO, numericConstants_1.ZERO]; | ||
} | ||
const takerIsLong = (0, types_2.isVariant)(direction, 'long'); | ||
const limitOrders = dlob[takerIsLong ? 'getRestingLimitAsks' : 'getRestingLimitBids'](market.marketIndex, slot, types_1.MarketType.PERP, oraclePriceData, minPerpAuctionDuration); | ||
const swapDirection = (0, amm_1.getSwapDirection)(assetType, direction); | ||
const { baseAssetReserve, quoteAssetReserve, sqrtK, newPeg } = (0, amm_1.calculateUpdatedAMMSpreadReserves)(market.amm, direction, oraclePriceData); | ||
const amm = { | ||
baseAssetReserve, | ||
quoteAssetReserve, | ||
sqrtK: sqrtK, | ||
pegMultiplier: newPeg, | ||
}; | ||
const invariant = amm.sqrtK.mul(amm.sqrtK); | ||
let initialPrice = (0, amm_1.calculatePrice)(amm.baseAssetReserve, amm.quoteAssetReserve, amm.pegMultiplier); | ||
let cumulativeBaseFilled = numericConstants_1.ZERO; | ||
let cumulativeQuoteFilled = numericConstants_1.ZERO; | ||
let limitOrder = limitOrders.next().value; | ||
if (limitOrder) { | ||
const limitOrderPrice = limitOrder.getPrice(oraclePriceData, slot); | ||
initialPrice = takerIsLong | ||
? anchor_1.BN.min(limitOrderPrice, initialPrice) | ||
: anchor_1.BN.max(limitOrderPrice, initialPrice); | ||
} | ||
if (assetType === 'base') { | ||
while (!cumulativeBaseFilled.eq(amount)) { | ||
const limitOrderPrice = limitOrder === null || limitOrder === void 0 ? void 0 : limitOrder.getPrice(oraclePriceData, slot); | ||
let maxAmmFill; | ||
if (limitOrderPrice) { | ||
const newBaseReserves = (0, utils_1.squareRootBN)(invariant | ||
.mul(numericConstants_1.PRICE_PRECISION) | ||
.mul(amm.pegMultiplier) | ||
.div(limitOrderPrice) | ||
.div(numericConstants_1.PEG_PRECISION)); | ||
// will be zero if the limit order price is better than the amm price | ||
maxAmmFill = takerIsLong | ||
? amm.baseAssetReserve.sub(newBaseReserves) | ||
: newBaseReserves.sub(amm.baseAssetReserve); | ||
} | ||
else { | ||
maxAmmFill = amount.sub(cumulativeBaseFilled); | ||
} | ||
if (maxAmmFill.gt(numericConstants_1.ZERO)) { | ||
const baseFilled = anchor_1.BN.min(amount.sub(cumulativeBaseFilled), maxAmmFill); | ||
const [afterSwapQuoteReserves, afterSwapBaseReserves] = (0, amm_1.calculateAmmReservesAfterSwap)(amm, 'base', baseFilled, swapDirection); | ||
const quoteFilled = (0, amm_1.calculateQuoteAssetAmountSwapped)(amm.quoteAssetReserve.sub(afterSwapQuoteReserves).abs(), amm.pegMultiplier, swapDirection); | ||
cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled); | ||
cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled); | ||
amm.baseAssetReserve = afterSwapBaseReserves; | ||
amm.quoteAssetReserve = afterSwapQuoteReserves; | ||
if (cumulativeBaseFilled.eq(amount)) { | ||
break; | ||
} | ||
} | ||
const baseFilled = anchor_1.BN.min(limitOrder.order.baseAssetAmount.sub(limitOrder.order.baseAssetAmountFilled), amount.sub(cumulativeBaseFilled)); | ||
const quoteFilled = baseFilled.mul(limitOrderPrice).div(numericConstants_1.BASE_PRECISION); | ||
cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled); | ||
cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled); | ||
if (cumulativeBaseFilled.eq(amount)) { | ||
break; | ||
} | ||
limitOrder = limitOrders.next().value; | ||
} | ||
} | ||
else { | ||
while (!cumulativeQuoteFilled.eq(amount)) { | ||
const limitOrderPrice = limitOrder === null || limitOrder === void 0 ? void 0 : limitOrder.getPrice(oraclePriceData, slot); | ||
let maxAmmFill; | ||
if (limitOrderPrice) { | ||
const newQuoteReserves = (0, utils_1.squareRootBN)(invariant | ||
.mul(numericConstants_1.PEG_PRECISION) | ||
.mul(limitOrderPrice) | ||
.div(amm.pegMultiplier) | ||
.div(numericConstants_1.PRICE_PRECISION)); | ||
// will be zero if the limit order price is better than the amm price | ||
maxAmmFill = takerIsLong | ||
? newQuoteReserves.sub(amm.quoteAssetReserve) | ||
: amm.quoteAssetReserve.sub(newQuoteReserves); | ||
} | ||
else { | ||
maxAmmFill = amount.sub(cumulativeQuoteFilled); | ||
} | ||
if (maxAmmFill.gt(numericConstants_1.ZERO)) { | ||
const quoteFilled = anchor_1.BN.min(amount.sub(cumulativeQuoteFilled), maxAmmFill); | ||
const [afterSwapQuoteReserves, afterSwapBaseReserves] = (0, amm_1.calculateAmmReservesAfterSwap)(amm, 'quote', quoteFilled, swapDirection); | ||
const baseFilled = afterSwapBaseReserves | ||
.sub(amm.baseAssetReserve) | ||
.abs(); | ||
cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled); | ||
cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled); | ||
amm.baseAssetReserve = afterSwapBaseReserves; | ||
amm.quoteAssetReserve = afterSwapQuoteReserves; | ||
if (cumulativeQuoteFilled.eq(amount)) { | ||
break; | ||
} | ||
} | ||
const quoteFilled = anchor_1.BN.min(limitOrder.order.baseAssetAmount | ||
.sub(limitOrder.order.baseAssetAmountFilled) | ||
.mul(limitOrderPrice) | ||
.div(numericConstants_1.BASE_PRECISION), amount.sub(cumulativeQuoteFilled)); | ||
const baseFilled = quoteFilled.mul(numericConstants_1.BASE_PRECISION).div(limitOrderPrice); | ||
cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled); | ||
cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled); | ||
if (cumulativeQuoteFilled.eq(amount)) { | ||
break; | ||
} | ||
limitOrder = limitOrders.next().value; | ||
} | ||
} | ||
const entryPrice = cumulativeQuoteFilled | ||
.mul(numericConstants_1.BASE_PRECISION) | ||
.div(cumulativeBaseFilled); | ||
const priceImpact = entryPrice | ||
.sub(initialPrice) | ||
.mul(numericConstants_1.PRICE_PRECISION) | ||
.div(initialPrice) | ||
.abs(); | ||
return [entryPrice, priceImpact]; | ||
} | ||
exports.calculateEstimatedPerpEntryPrice = calculateEstimatedPerpEntryPrice; |
{ | ||
"name": "@drift-labs/sdk", | ||
"version": "2.12.0-beta.0", | ||
"version": "2.12.0-beta.1", | ||
"main": "lib/index.js", | ||
@@ -5,0 +5,0 @@ "types": "lib/index.d.ts", |
109
README.md
@@ -14,10 +14,2 @@ <div align="center"> | ||
# Drift Protocol v2 | ||
This repository provides open source access to Drift's Typescript SDK, Solana Programs, and more. | ||
# SDK Guide | ||
The technical documentation for the SDK can be found [here](https://drift-labs.github.io/protocol-v2/sdk/), and you can visit Drift's general purpose documentation [here](https://docs.drift.trade/sdk-documentation). | ||
## Installation | ||
@@ -88,3 +80,3 @@ | ||
```typescript | ||
import { BN, Provider } from '@project-serum/anchor'; | ||
import { AnchorProvider, BN } from '@project-serum/anchor'; | ||
import { Token, TOKEN_PROGRAM_ID } from '@solana/spl-token'; | ||
@@ -97,3 +89,2 @@ import { Connection, Keypair, PublicKey } from '@solana/web3.js'; | ||
initialize, | ||
Markets, | ||
PositionDirection, | ||
@@ -105,3 +96,8 @@ convertToNumber, | ||
Wallet, | ||
} from '@drift-labs/sdk'; | ||
PerpMarkets, | ||
BASE_PRECISION, | ||
getMarketOrderParams, | ||
BulkAccountLoader, | ||
getMarketsAndOraclesForSubscription | ||
} from '../sdk'; | ||
@@ -121,4 +117,5 @@ export const getTokenAddress = ( | ||
const main = async () => { | ||
const env = 'devnet'; | ||
// Initialize Drift SDK | ||
const sdkConfig = initialize({ env: 'devnet' }); | ||
const sdkConfig = initialize({ env }); | ||
@@ -137,3 +134,7 @@ // Set up the Wallet and Provider | ||
// Set up the Provider | ||
const provider = new Provider(connection, wallet, Provider.defaultOptions()); | ||
const provider = new AnchorProvider( | ||
connection, | ||
wallet, | ||
AnchorProvider.defaultOptions() | ||
); | ||
@@ -151,14 +152,31 @@ // Check SOL Balance | ||
// Set up the Drift Client | ||
const driftClientPublicKey = new PublicKey(sdkConfig.DRIFT_PROGRAM_ID); | ||
const driftClient = DriftClient.from( | ||
const driftPublicKey = new PublicKey(sdkConfig.DRIFT_PROGRAM_ID); | ||
const bulkAccountLoader = new BulkAccountLoader( | ||
connection, | ||
provider.wallet, | ||
driftClientPublicKey | ||
'confirmed', | ||
1000 | ||
); | ||
const driftClient = new DriftClient({ | ||
connection, | ||
wallet: provider.wallet, | ||
programID: driftPublicKey, | ||
...getMarketsAndOraclesForSubscription(env), | ||
accountSubscription: { | ||
type: 'polling', | ||
accountLoader: bulkAccountLoader, | ||
}, | ||
}); | ||
await driftClient.subscribe(); | ||
// Set up Clearing House user client | ||
const user = User.from(driftClient, wallet.publicKey); | ||
// Set up user client | ||
const user = new User({ | ||
driftClient: driftClient, | ||
userAccountPublicKey: await driftClient.getUserAccountPublicKey(), | ||
accountSubscription: { | ||
type: 'polling', | ||
accountLoader: bulkAccountLoader, | ||
}, | ||
}); | ||
//// Check if clearing house account exists for the current wallet | ||
//// Check if user account exists for the current wallet | ||
const userAccountExists = await user.exists(); | ||
@@ -181,16 +199,22 @@ | ||
// Get current price | ||
const solMarketInfo = Markets.find( | ||
const solMarketInfo = PerpMarkets[env].find( | ||
(market) => market.baseAssetSymbol === 'SOL' | ||
); | ||
const currentMarketPrice = calculateReservePrice( | ||
driftClient.getMarket(solMarketInfo.marketIndex) | ||
const [bid, ask] = calculateBidAskPrice( | ||
driftClient.getPerpMarketAccount(marketIndex).amm, | ||
driftClient.getOracleDataForPerpMarket(marketIndex) | ||
); | ||
const formattedPrice = convertToNumber(currentMarketPrice, PRICE_PRECISION); | ||
const formattedBidPrice = convertToNumber(bid, PRICE_PRECISION); | ||
const formattedAskPrice = convertToNumber(ask, PRICE_PRECISION); | ||
console.log(`Current Market Price is $${formattedPrice}`); | ||
console.log( | ||
`Current amm bid and ask price are $${formattedBidPrice} and $${formattedAskPrice}` | ||
); | ||
// Estimate the slippage for a $5000 LONG trade | ||
const solMarketAccount = driftClient.getMarket(solMarketInfo.marketIndex); | ||
const solMarketAccount = driftClient.getPerpMarketAccount( | ||
solMarketInfo.marketIndex | ||
); | ||
@@ -200,4 +224,6 @@ const slippage = convertToNumber( | ||
PositionDirection.LONG, | ||
new BN(5000).mul(QUOTE_PRECISION), | ||
solMarketAccount | ||
new BN(1).mul(BASE_PRECISION), | ||
solMarketAccount, | ||
'base', | ||
driftClient.getOracleDataForPerpMarket(solMarketInfo.marketIndex) | ||
)[0], | ||
@@ -207,23 +233,12 @@ PRICE_PRECISION | ||
console.log( | ||
`Slippage for a $5000 LONG on the SOL market would be $${slippage}` | ||
); | ||
console.log(`Slippage for a 1 SOL-PERP would be $${slippage}`); | ||
// Make a $5000 LONG trade | ||
await driftClient.openPosition( | ||
PositionDirection.LONG, | ||
new BN(5000).mul(QUOTE_PRECISION), | ||
solMarketInfo.marketIndex | ||
await driftClient.placePerpOrder( | ||
getMarketOrderParams({ | ||
baseAssetAmount: new BN(1).mul(BASE_PRECISION), | ||
direction: PositionDirection.LONG, | ||
marketIndex: solMarketAccount.marketIndex, | ||
}) | ||
); | ||
console.log(`LONGED $5000 worth of SOL`); | ||
// Reduce the position by $2000 | ||
await driftClient.openPosition( | ||
PositionDirection.SHORT, | ||
new BN(2000).mul(QUOTE_PRECISION), | ||
solMarketInfo.marketIndex | ||
); | ||
// Close the rest of the position | ||
await driftClient.closePosition(solMarketInfo.marketIndex); | ||
console.log(`Placed a 1 SOL-PERP LONG order`); | ||
}; | ||
@@ -230,0 +245,0 @@ |
import { AnchorProvider, BN } from '@project-serum/anchor'; | ||
import { Wallet } from '..'; | ||
import { | ||
BASE_PRECISION, | ||
calculateBidAskPrice, | ||
getMarketOrderParams, | ||
Wallet, | ||
} from '..'; | ||
import { Token, TOKEN_PROGRAM_ID } from '@solana/spl-token'; | ||
import { Connection, Keypair, PublicKey } from '@solana/web3.js'; | ||
import { | ||
calculateReservePrice, | ||
DriftClient, | ||
@@ -32,7 +36,7 @@ User, | ||
const cluster = 'devnet'; | ||
const env = 'devnet'; | ||
const main = async () => { | ||
// Initialize Drift SDK | ||
const sdkConfig = initialize({ env: cluster }); | ||
const sdkConfig = initialize({ env }); | ||
@@ -78,3 +82,3 @@ // Set up the Wallet and Provider | ||
programID: driftPublicKey, | ||
...getMarketsAndOraclesForSubscription(cluster), | ||
...getMarketsAndOraclesForSubscription(env), | ||
accountSubscription: { | ||
@@ -87,3 +91,3 @@ type: 'polling', | ||
// Set up Clearing House user client | ||
// Set up user client | ||
const user = new User({ | ||
@@ -98,3 +102,3 @@ driftClient: driftClient, | ||
//// Check if clearing house account exists for the current wallet | ||
//// Check if user account exists for the current wallet | ||
const userAccountExists = await user.exists(); | ||
@@ -122,10 +126,14 @@ | ||
const currentMarketPrice = calculateReservePrice( | ||
driftClient.getPerpMarketAccount(solMarketInfo.marketIndex), | ||
undefined | ||
const marketIndex = solMarketInfo.marketIndex; | ||
const [bid, ask] = calculateBidAskPrice( | ||
driftClient.getPerpMarketAccount(marketIndex).amm, | ||
driftClient.getOracleDataForPerpMarket(marketIndex) | ||
); | ||
const formattedPrice = convertToNumber(currentMarketPrice, PRICE_PRECISION); | ||
const formattedBidPrice = convertToNumber(bid, PRICE_PRECISION); | ||
const formattedAskPrice = convertToNumber(ask, PRICE_PRECISION); | ||
console.log(`Current Market Price is $${formattedPrice}`); | ||
console.log( | ||
`Current amm bid and ask price are $${formattedBidPrice} and $${formattedAskPrice}` | ||
); | ||
@@ -137,9 +145,8 @@ // Estimate the slippage for a $5000 LONG trade | ||
const longAmount = new BN(5000).mul(QUOTE_PRECISION); | ||
const slippage = convertToNumber( | ||
calculateTradeSlippage( | ||
PositionDirection.LONG, | ||
longAmount, | ||
new BN(1).mul(BASE_PRECISION), | ||
solMarketAccount, | ||
'quote', | ||
'base', | ||
driftClient.getOracleDataForPerpMarket(solMarketInfo.marketIndex) | ||
@@ -150,26 +157,14 @@ )[0], | ||
console.log( | ||
`Slippage for a $5000 LONG on the SOL market would be $${slippage}` | ||
); | ||
console.log(`Slippage for a 1 SOL-PERP would be $${slippage}`); | ||
// Make a $5000 LONG trade | ||
await driftClient.openPosition( | ||
PositionDirection.LONG, | ||
longAmount, | ||
solMarketInfo.marketIndex | ||
await driftClient.placePerpOrder( | ||
getMarketOrderParams({ | ||
baseAssetAmount: new BN(1).mul(BASE_PRECISION), | ||
direction: PositionDirection.LONG, | ||
marketIndex: solMarketAccount.marketIndex, | ||
}) | ||
); | ||
console.log(`LONGED $5000 SOL`); | ||
// Reduce the position by $2000 | ||
const reduceAmount = new BN(2000).mul(QUOTE_PRECISION); | ||
await driftClient.openPosition( | ||
PositionDirection.SHORT, | ||
reduceAmount, | ||
solMarketInfo.marketIndex | ||
); | ||
// Close the rest of the position | ||
await driftClient.closePosition(solMarketInfo.marketIndex); | ||
console.log(`Placed a 1 SOL-PERP LONG order`); | ||
}; | ||
main(); |
@@ -1,2 +0,2 @@ | ||
import { PerpMarketAccount, PositionDirection } from '../types'; | ||
import { MarketType, PerpMarketAccount, PositionDirection } from '../types'; | ||
import { BN } from '@project-serum/anchor'; | ||
@@ -9,2 +9,3 @@ import { assert } from '../assert/assert'; | ||
ZERO, | ||
BASE_PRECISION, | ||
} from '../constants/numericConstants'; | ||
@@ -27,2 +28,3 @@ import { | ||
import { OraclePriceData } from '../oracles/types'; | ||
import { DLOB } from '../dlob/DLOB'; | ||
@@ -354,1 +356,214 @@ const MAXPCT = new BN(1000); //percentage units are [0,1000] => [0,1] | ||
} | ||
/** | ||
* Calculates the estimated entry price and price impact of order, in base or quote | ||
* Price impact is based on the difference between the entry price and the best bid/ask price (whether it's dlob or vamm) | ||
* | ||
* @param assetType | ||
* @param amount | ||
* @param direction | ||
* @param market | ||
* @param oraclePriceData | ||
* @param dlob | ||
* @param slot | ||
* @param minPerpAuctionDuration | ||
*/ | ||
export function calculateEstimatedPerpEntryPrice( | ||
assetType: AssetType, | ||
amount: BN, | ||
direction: PositionDirection, | ||
market: PerpMarketAccount, | ||
oraclePriceData: OraclePriceData, | ||
dlob: DLOB, | ||
slot: number, | ||
minPerpAuctionDuration: number | ||
): [BN, BN] { | ||
if (amount.eq(ZERO)) { | ||
return [ZERO, ZERO]; | ||
} | ||
const takerIsLong = isVariant(direction, 'long'); | ||
const limitOrders = dlob[ | ||
takerIsLong ? 'getRestingLimitAsks' : 'getRestingLimitBids' | ||
]( | ||
market.marketIndex, | ||
slot, | ||
MarketType.PERP, | ||
oraclePriceData, | ||
minPerpAuctionDuration | ||
); | ||
const swapDirection = getSwapDirection(assetType, direction); | ||
const { baseAssetReserve, quoteAssetReserve, sqrtK, newPeg } = | ||
calculateUpdatedAMMSpreadReserves(market.amm, direction, oraclePriceData); | ||
const amm = { | ||
baseAssetReserve, | ||
quoteAssetReserve, | ||
sqrtK: sqrtK, | ||
pegMultiplier: newPeg, | ||
}; | ||
const invariant = amm.sqrtK.mul(amm.sqrtK); | ||
let initialPrice = calculatePrice( | ||
amm.baseAssetReserve, | ||
amm.quoteAssetReserve, | ||
amm.pegMultiplier | ||
); | ||
let cumulativeBaseFilled = ZERO; | ||
let cumulativeQuoteFilled = ZERO; | ||
let limitOrder = limitOrders.next().value; | ||
if (limitOrder) { | ||
const limitOrderPrice = limitOrder.getPrice(oraclePriceData, slot); | ||
initialPrice = takerIsLong | ||
? BN.min(limitOrderPrice, initialPrice) | ||
: BN.max(limitOrderPrice, initialPrice); | ||
} | ||
if (assetType === 'base') { | ||
while (!cumulativeBaseFilled.eq(amount)) { | ||
const limitOrderPrice = limitOrder?.getPrice(oraclePriceData, slot); | ||
let maxAmmFill: BN; | ||
if (limitOrderPrice) { | ||
const newBaseReserves = squareRootBN( | ||
invariant | ||
.mul(PRICE_PRECISION) | ||
.mul(amm.pegMultiplier) | ||
.div(limitOrderPrice) | ||
.div(PEG_PRECISION) | ||
); | ||
// will be zero if the limit order price is better than the amm price | ||
maxAmmFill = takerIsLong | ||
? amm.baseAssetReserve.sub(newBaseReserves) | ||
: newBaseReserves.sub(amm.baseAssetReserve); | ||
} else { | ||
maxAmmFill = amount.sub(cumulativeBaseFilled); | ||
} | ||
if (maxAmmFill.gt(ZERO)) { | ||
const baseFilled = BN.min(amount.sub(cumulativeBaseFilled), maxAmmFill); | ||
const [afterSwapQuoteReserves, afterSwapBaseReserves] = | ||
calculateAmmReservesAfterSwap(amm, 'base', baseFilled, swapDirection); | ||
const quoteFilled = calculateQuoteAssetAmountSwapped( | ||
amm.quoteAssetReserve.sub(afterSwapQuoteReserves).abs(), | ||
amm.pegMultiplier, | ||
swapDirection | ||
); | ||
cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled); | ||
cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled); | ||
amm.baseAssetReserve = afterSwapBaseReserves; | ||
amm.quoteAssetReserve = afterSwapQuoteReserves; | ||
if (cumulativeBaseFilled.eq(amount)) { | ||
break; | ||
} | ||
} | ||
const baseFilled = BN.min( | ||
limitOrder.order.baseAssetAmount.sub( | ||
limitOrder.order.baseAssetAmountFilled | ||
), | ||
amount.sub(cumulativeBaseFilled) | ||
); | ||
const quoteFilled = baseFilled.mul(limitOrderPrice).div(BASE_PRECISION); | ||
cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled); | ||
cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled); | ||
if (cumulativeBaseFilled.eq(amount)) { | ||
break; | ||
} | ||
limitOrder = limitOrders.next().value; | ||
} | ||
} else { | ||
while (!cumulativeQuoteFilled.eq(amount)) { | ||
const limitOrderPrice = limitOrder?.getPrice(oraclePriceData, slot); | ||
let maxAmmFill: BN; | ||
if (limitOrderPrice) { | ||
const newQuoteReserves = squareRootBN( | ||
invariant | ||
.mul(PEG_PRECISION) | ||
.mul(limitOrderPrice) | ||
.div(amm.pegMultiplier) | ||
.div(PRICE_PRECISION) | ||
); | ||
// will be zero if the limit order price is better than the amm price | ||
maxAmmFill = takerIsLong | ||
? newQuoteReserves.sub(amm.quoteAssetReserve) | ||
: amm.quoteAssetReserve.sub(newQuoteReserves); | ||
} else { | ||
maxAmmFill = amount.sub(cumulativeQuoteFilled); | ||
} | ||
if (maxAmmFill.gt(ZERO)) { | ||
const quoteFilled = BN.min( | ||
amount.sub(cumulativeQuoteFilled), | ||
maxAmmFill | ||
); | ||
const [afterSwapQuoteReserves, afterSwapBaseReserves] = | ||
calculateAmmReservesAfterSwap( | ||
amm, | ||
'quote', | ||
quoteFilled, | ||
swapDirection | ||
); | ||
const baseFilled = afterSwapBaseReserves | ||
.sub(amm.baseAssetReserve) | ||
.abs(); | ||
cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled); | ||
cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled); | ||
amm.baseAssetReserve = afterSwapBaseReserves; | ||
amm.quoteAssetReserve = afterSwapQuoteReserves; | ||
if (cumulativeQuoteFilled.eq(amount)) { | ||
break; | ||
} | ||
} | ||
const quoteFilled = BN.min( | ||
limitOrder.order.baseAssetAmount | ||
.sub(limitOrder.order.baseAssetAmountFilled) | ||
.mul(limitOrderPrice) | ||
.div(BASE_PRECISION), | ||
amount.sub(cumulativeQuoteFilled) | ||
); | ||
const baseFilled = quoteFilled.mul(BASE_PRECISION).div(limitOrderPrice); | ||
cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled); | ||
cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled); | ||
if (cumulativeQuoteFilled.eq(amount)) { | ||
break; | ||
} | ||
limitOrder = limitOrders.next().value; | ||
} | ||
} | ||
const entryPrice = cumulativeQuoteFilled | ||
.mul(BASE_PRECISION) | ||
.div(cumulativeBaseFilled); | ||
const priceImpact = entryPrice | ||
.sub(initialPrice) | ||
.mul(PRICE_PRECISION) | ||
.div(initialPrice) | ||
.abs(); | ||
return [entryPrice, priceImpact]; | ||
} |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
1802496
56182
247