add check that slippage crosses bba (#548)
* add check that slippage crosses bba * fix tests
This commit is contained in:
@@ -727,6 +727,7 @@ describe('calculateDynamicSlippage - crossed book handling', () => {
|
|||||||
const worstPrice = new BN(100).mul(PRICE_PRECISION);
|
const worstPrice = new BN(100).mul(PRICE_PRECISION);
|
||||||
|
|
||||||
const slip = calculateDynamicSlippage(
|
const slip = calculateDynamicSlippage(
|
||||||
|
'long',
|
||||||
0, // major perp
|
0, // major perp
|
||||||
'perp',
|
'perp',
|
||||||
mockDriftClient,
|
mockDriftClient,
|
||||||
@@ -761,6 +762,7 @@ describe('calculateDynamicSlippage - crossed book handling', () => {
|
|||||||
const worstPrice = new BN(100).mul(PRICE_PRECISION);
|
const worstPrice = new BN(100).mul(PRICE_PRECISION);
|
||||||
|
|
||||||
const slipNormal = calculateDynamicSlippage(
|
const slipNormal = calculateDynamicSlippage(
|
||||||
|
'long',
|
||||||
0,
|
0,
|
||||||
'perp',
|
'perp',
|
||||||
mockDriftClient,
|
mockDriftClient,
|
||||||
@@ -775,6 +777,7 @@ describe('calculateDynamicSlippage - crossed book handling', () => {
|
|||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
const slipCrossed = calculateDynamicSlippage(
|
const slipCrossed = calculateDynamicSlippage(
|
||||||
|
'long',
|
||||||
0,
|
0,
|
||||||
'perp',
|
'perp',
|
||||||
mockDriftClient,
|
mockDriftClient,
|
||||||
@@ -787,3 +790,338 @@ describe('calculateDynamicSlippage - crossed book handling', () => {
|
|||||||
expect(slipNormal).toBeGreaterThanOrEqual(slipCrossed);
|
expect(slipNormal).toBeGreaterThanOrEqual(slipCrossed);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('calculateDynamicSlippage - 1bp best bid/ask adjustment', () => {
|
||||||
|
const mockDriftClient = {
|
||||||
|
getMMOracleDataForPerpMarket: jest.fn(),
|
||||||
|
getOracleDataForSpotMarket: jest.fn(),
|
||||||
|
} as any;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
// Set deterministic env values
|
||||||
|
process.env.DYNAMIC_BASE_SLIPPAGE_MAJOR = '0';
|
||||||
|
process.env.DYNAMIC_SLIPPAGE_MULTIPLIER_MAJOR = '1';
|
||||||
|
process.env.DYNAMIC_SLIPPAGE_MIN = '0.01'; // 0.01% minimum
|
||||||
|
process.env.DYNAMIC_SLIPPAGE_MAX = '100';
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should adjust slippage for LONG when impliedEndPrice < bestAskPrice', () => {
|
||||||
|
// Set very tight env to minimize base slippage
|
||||||
|
process.env.DYNAMIC_BASE_SLIPPAGE_MAJOR = '0';
|
||||||
|
process.env.DYNAMIC_SLIPPAGE_MIN = '0';
|
||||||
|
|
||||||
|
const oraclePrice = new BN(100).mul(PRICE_PRECISION); // $100
|
||||||
|
const bestBidPrice = new BN(100).mul(PRICE_PRECISION); // $100 (tight spread)
|
||||||
|
const bestAskPrice = new BN(101).mul(PRICE_PRECISION); // $101
|
||||||
|
const startPrice = new BN(99).mul(PRICE_PRECISION); // $99 (start below ask)
|
||||||
|
|
||||||
|
mockDriftClient.getMMOracleDataForPerpMarket.mockReturnValue({
|
||||||
|
price: oraclePrice,
|
||||||
|
});
|
||||||
|
|
||||||
|
const l2 = {
|
||||||
|
bids: [{ price: bestBidPrice, size: new BN(1) }],
|
||||||
|
asks: [{ price: bestAskPrice, size: new BN(1) }],
|
||||||
|
} as any;
|
||||||
|
|
||||||
|
// With start price at 99 and very small worst price, initial slippage will be tiny
|
||||||
|
const worstPrice = new BN(99100000); // $99.10 in PRICE_PRECISION
|
||||||
|
|
||||||
|
const slippage = calculateDynamicSlippage(
|
||||||
|
'long',
|
||||||
|
0, // major perp
|
||||||
|
'perp',
|
||||||
|
mockDriftClient,
|
||||||
|
l2,
|
||||||
|
startPrice,
|
||||||
|
worstPrice
|
||||||
|
);
|
||||||
|
|
||||||
|
// Calculate what the implied end price would be
|
||||||
|
const impliedEndPrice = startPrice.toNumber() * (1 + slippage / 100);
|
||||||
|
|
||||||
|
// Verify that impliedEndPrice is at least 1bp greater than bestAskPrice
|
||||||
|
const targetPrice = bestAskPrice.toNumber() * 1.0001; // 1bp above bestAsk
|
||||||
|
expect(impliedEndPrice).toBeGreaterThanOrEqual(targetPrice);
|
||||||
|
|
||||||
|
// With startPrice at 99 and bestAsk at 101, we need at least 2.02% slippage
|
||||||
|
// to reach 101 * 1.0001 = 101.0101
|
||||||
|
expect(slippage).toBeGreaterThan(2.0);
|
||||||
|
expect(slippage).toBeLessThan(3.0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should adjust slippage for SHORT when impliedEndPrice > bestBidPrice', () => {
|
||||||
|
// Set very tight env to minimize base slippage
|
||||||
|
process.env.DYNAMIC_BASE_SLIPPAGE_MAJOR = '0';
|
||||||
|
process.env.DYNAMIC_SLIPPAGE_MIN = '0';
|
||||||
|
|
||||||
|
const oraclePrice = new BN(100).mul(PRICE_PRECISION); // $100
|
||||||
|
const bestBidPrice = new BN(99).mul(PRICE_PRECISION); // $99
|
||||||
|
const bestAskPrice = new BN(100).mul(PRICE_PRECISION); // $100 (tight spread)
|
||||||
|
const startPrice = new BN(101).mul(PRICE_PRECISION); // $101 (start above bid)
|
||||||
|
|
||||||
|
mockDriftClient.getMMOracleDataForPerpMarket.mockReturnValue({
|
||||||
|
price: oraclePrice,
|
||||||
|
});
|
||||||
|
|
||||||
|
const l2 = {
|
||||||
|
bids: [{ price: bestBidPrice, size: new BN(1) }],
|
||||||
|
asks: [{ price: bestAskPrice, size: new BN(1) }],
|
||||||
|
} as any;
|
||||||
|
|
||||||
|
// With start price at 101 and very small worst price, initial slippage will be tiny
|
||||||
|
const worstPrice = new BN(100900000); // $100.90 in PRICE_PRECISION
|
||||||
|
|
||||||
|
const slippage = calculateDynamicSlippage(
|
||||||
|
'short',
|
||||||
|
0, // major perp
|
||||||
|
'perp',
|
||||||
|
mockDriftClient,
|
||||||
|
l2,
|
||||||
|
startPrice,
|
||||||
|
worstPrice
|
||||||
|
);
|
||||||
|
|
||||||
|
// Calculate what the implied end price would be
|
||||||
|
const impliedEndPrice = startPrice.toNumber() * (1 - slippage / 100);
|
||||||
|
|
||||||
|
// Verify that impliedEndPrice is at least 1bp less than bestBidPrice
|
||||||
|
const targetPrice = bestBidPrice.toNumber() * 0.9999; // 1bp below bestBid
|
||||||
|
expect(impliedEndPrice).toBeLessThanOrEqual(targetPrice);
|
||||||
|
|
||||||
|
// With startPrice at 101 and bestBid at 99, we need at least 1.99% slippage
|
||||||
|
// to reach 99 * 0.9999 = 98.9901
|
||||||
|
expect(slippage).toBeGreaterThan(1.98);
|
||||||
|
expect(slippage).toBeLessThan(3.0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should NOT adjust slippage for LONG when impliedEndPrice already > bestAskPrice', () => {
|
||||||
|
const oraclePrice = new BN(100).mul(PRICE_PRECISION); // $100
|
||||||
|
const bestBidPrice = new BN(99).mul(PRICE_PRECISION); // $99
|
||||||
|
const bestAskPrice = new BN(101).mul(PRICE_PRECISION); // $101
|
||||||
|
const startPrice = new BN(100).mul(PRICE_PRECISION); // $100
|
||||||
|
|
||||||
|
mockDriftClient.getMMOracleDataForPerpMarket.mockReturnValue({
|
||||||
|
price: oraclePrice,
|
||||||
|
});
|
||||||
|
|
||||||
|
const l2 = {
|
||||||
|
bids: [{ price: bestBidPrice, size: new BN(1) }],
|
||||||
|
asks: [{ price: bestAskPrice, size: new BN(1) }],
|
||||||
|
} as any;
|
||||||
|
|
||||||
|
// With large worst price, impliedEndPrice will already be > bestAskPrice
|
||||||
|
const worstPrice = new BN(105).mul(PRICE_PRECISION); // $105 - large slippage
|
||||||
|
|
||||||
|
const slippage = calculateDynamicSlippage(
|
||||||
|
'long',
|
||||||
|
0, // major perp
|
||||||
|
'perp',
|
||||||
|
mockDriftClient,
|
||||||
|
l2,
|
||||||
|
startPrice,
|
||||||
|
worstPrice
|
||||||
|
);
|
||||||
|
|
||||||
|
// Calculate what the implied end price would be
|
||||||
|
const impliedEndPrice = startPrice.toNumber() * (1 + slippage / 100);
|
||||||
|
|
||||||
|
// Should already be well above bestAskPrice
|
||||||
|
expect(impliedEndPrice).toBeGreaterThan(bestAskPrice.toNumber());
|
||||||
|
|
||||||
|
// Slippage should be driven by the size-adjusted calculation (halfway to worst)
|
||||||
|
// Expected: ((100 - 105) / 100 / 2) * 100 = 2.5%
|
||||||
|
expect(slippage).toBeGreaterThan(2.0);
|
||||||
|
expect(slippage).toBeLessThan(3.0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should NOT adjust slippage for SHORT when impliedEndPrice already < bestBidPrice', () => {
|
||||||
|
const oraclePrice = new BN(100).mul(PRICE_PRECISION); // $100
|
||||||
|
const bestBidPrice = new BN(99).mul(PRICE_PRECISION); // $99
|
||||||
|
const bestAskPrice = new BN(101).mul(PRICE_PRECISION); // $101
|
||||||
|
const startPrice = new BN(100).mul(PRICE_PRECISION); // $100
|
||||||
|
|
||||||
|
mockDriftClient.getMMOracleDataForPerpMarket.mockReturnValue({
|
||||||
|
price: oraclePrice,
|
||||||
|
});
|
||||||
|
|
||||||
|
const l2 = {
|
||||||
|
bids: [{ price: bestBidPrice, size: new BN(1) }],
|
||||||
|
asks: [{ price: bestAskPrice, size: new BN(1) }],
|
||||||
|
} as any;
|
||||||
|
|
||||||
|
// With large worst price, impliedEndPrice will already be < bestBidPrice
|
||||||
|
const worstPrice = new BN(95).mul(PRICE_PRECISION); // $95 - large slippage
|
||||||
|
|
||||||
|
const slippage = calculateDynamicSlippage(
|
||||||
|
'short',
|
||||||
|
0, // major perp
|
||||||
|
'perp',
|
||||||
|
mockDriftClient,
|
||||||
|
l2,
|
||||||
|
startPrice,
|
||||||
|
worstPrice
|
||||||
|
);
|
||||||
|
|
||||||
|
// Calculate what the implied end price would be
|
||||||
|
const impliedEndPrice = startPrice.toNumber() * (1 - slippage / 100);
|
||||||
|
|
||||||
|
// Should already be well below bestBidPrice
|
||||||
|
expect(impliedEndPrice).toBeLessThan(bestBidPrice.toNumber());
|
||||||
|
|
||||||
|
// Slippage should be driven by the size-adjusted calculation
|
||||||
|
expect(slippage).toBeGreaterThan(2.0);
|
||||||
|
expect(slippage).toBeLessThan(3.0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle tight spread with precise 1bp adjustment for LONG', () => {
|
||||||
|
const oraclePrice = new BN(100).mul(PRICE_PRECISION); // $100
|
||||||
|
const bestBidPrice = new BN(99950000); // $99.95 in PRICE_PRECISION
|
||||||
|
const bestAskPrice = new BN(100050000); // $100.05 in PRICE_PRECISION (5bp spread)
|
||||||
|
const startPrice = new BN(100).mul(PRICE_PRECISION); // $100
|
||||||
|
|
||||||
|
mockDriftClient.getMMOracleDataForPerpMarket.mockReturnValue({
|
||||||
|
price: oraclePrice,
|
||||||
|
});
|
||||||
|
|
||||||
|
const l2 = {
|
||||||
|
bids: [{ price: bestBidPrice, size: new BN(1) }],
|
||||||
|
asks: [{ price: bestAskPrice, size: new BN(1) }],
|
||||||
|
} as any;
|
||||||
|
|
||||||
|
const worstPrice = new BN(100010000); // $100.01 in PRICE_PRECISION
|
||||||
|
|
||||||
|
const slippage = calculateDynamicSlippage(
|
||||||
|
'long',
|
||||||
|
0,
|
||||||
|
'perp',
|
||||||
|
mockDriftClient,
|
||||||
|
l2,
|
||||||
|
startPrice,
|
||||||
|
worstPrice
|
||||||
|
);
|
||||||
|
|
||||||
|
// Calculate implied end price
|
||||||
|
const impliedEndPrice = startPrice.toNumber() * (1 + slippage / 100);
|
||||||
|
const targetPrice = bestAskPrice.toNumber() * 1.0001;
|
||||||
|
|
||||||
|
// Should be adjusted to go 1bp past bestAsk
|
||||||
|
expect(impliedEndPrice).toBeGreaterThanOrEqual(targetPrice);
|
||||||
|
|
||||||
|
// The slippage will be influenced by spread calculation (5bp spread * 0.9 = 0.45%)
|
||||||
|
// plus size adjustment, so it will be higher than just the 1bp adjustment
|
||||||
|
expect(slippage).toBeGreaterThan(0.05);
|
||||||
|
expect(slippage).toBeLessThan(1.5);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle tight spread with precise 1bp adjustment for SHORT', () => {
|
||||||
|
const oraclePrice = new BN(100).mul(PRICE_PRECISION); // $100
|
||||||
|
const bestBidPrice = new BN(99950000); // $99.95 in PRICE_PRECISION
|
||||||
|
const bestAskPrice = new BN(100050000); // $100.05 in PRICE_PRECISION (5bp spread)
|
||||||
|
const startPrice = new BN(100).mul(PRICE_PRECISION); // $100
|
||||||
|
|
||||||
|
mockDriftClient.getMMOracleDataForPerpMarket.mockReturnValue({
|
||||||
|
price: oraclePrice,
|
||||||
|
});
|
||||||
|
|
||||||
|
const l2 = {
|
||||||
|
bids: [{ price: bestBidPrice, size: new BN(1) }],
|
||||||
|
asks: [{ price: bestAskPrice, size: new BN(1) }],
|
||||||
|
} as any;
|
||||||
|
|
||||||
|
const worstPrice = new BN(99990000); // $99.99 in PRICE_PRECISION
|
||||||
|
|
||||||
|
const slippage = calculateDynamicSlippage(
|
||||||
|
'short',
|
||||||
|
0,
|
||||||
|
'perp',
|
||||||
|
mockDriftClient,
|
||||||
|
l2,
|
||||||
|
startPrice,
|
||||||
|
worstPrice
|
||||||
|
);
|
||||||
|
|
||||||
|
// Calculate implied end price
|
||||||
|
const impliedEndPrice = startPrice.toNumber() * (1 - slippage / 100);
|
||||||
|
const targetPrice = bestBidPrice.toNumber() * 0.9999;
|
||||||
|
|
||||||
|
// Should be adjusted to go 1bp past bestBid
|
||||||
|
expect(impliedEndPrice).toBeLessThanOrEqual(targetPrice);
|
||||||
|
|
||||||
|
// The slippage will be influenced by spread calculation (5bp spread * 0.9 = 0.45%)
|
||||||
|
// plus the 1bp adjustment needed, so it will be higher than just 0.06%
|
||||||
|
expect(slippage).toBeGreaterThan(0.05);
|
||||||
|
expect(slippage).toBeLessThan(1.5);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle crossed orderbook for LONG', () => {
|
||||||
|
const oraclePrice = new BN(100).mul(PRICE_PRECISION); // $100
|
||||||
|
const bestBidPrice = new BN(101).mul(PRICE_PRECISION); // $101 (crossed)
|
||||||
|
const bestAskPrice = new BN(99).mul(PRICE_PRECISION); // $99 (crossed)
|
||||||
|
const startPrice = new BN(100).mul(PRICE_PRECISION); // $100
|
||||||
|
|
||||||
|
mockDriftClient.getMMOracleDataForPerpMarket.mockReturnValue({
|
||||||
|
price: oraclePrice,
|
||||||
|
});
|
||||||
|
|
||||||
|
const l2 = {
|
||||||
|
bids: [{ price: bestBidPrice, size: new BN(1) }],
|
||||||
|
asks: [{ price: bestAskPrice, size: new BN(1) }],
|
||||||
|
} as any;
|
||||||
|
|
||||||
|
const worstPrice = new BN(100010000); // $100.01 in PRICE_PRECISION
|
||||||
|
|
||||||
|
const slippage = calculateDynamicSlippage(
|
||||||
|
'long',
|
||||||
|
0,
|
||||||
|
'perp',
|
||||||
|
mockDriftClient,
|
||||||
|
l2,
|
||||||
|
startPrice,
|
||||||
|
worstPrice
|
||||||
|
);
|
||||||
|
|
||||||
|
// Even with crossed book, should still calculate correctly
|
||||||
|
// bestAskPrice is 99, so targetPrice = 99 * 1.0001 = 99.0099
|
||||||
|
// impliedEndPrice should be >= 99.0099
|
||||||
|
const impliedEndPrice = startPrice.toNumber() * (1 + slippage / 100);
|
||||||
|
|
||||||
|
// Since bestAsk (99) < startPrice (100), the adjustment shouldn't trigger
|
||||||
|
// because impliedEndPrice will already be > bestAsk
|
||||||
|
expect(impliedEndPrice).toBeGreaterThan(bestAskPrice.toNumber());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should respect minimum slippage even with adjustment', () => {
|
||||||
|
process.env.DYNAMIC_SLIPPAGE_MIN = '2.0'; // 2% minimum
|
||||||
|
|
||||||
|
const oraclePrice = new BN(100).mul(PRICE_PRECISION);
|
||||||
|
const bestBidPrice = new BN(99).mul(PRICE_PRECISION);
|
||||||
|
const bestAskPrice = new BN(101).mul(PRICE_PRECISION);
|
||||||
|
const startPrice = new BN(100).mul(PRICE_PRECISION);
|
||||||
|
|
||||||
|
mockDriftClient.getMMOracleDataForPerpMarket.mockReturnValue({
|
||||||
|
price: oraclePrice,
|
||||||
|
});
|
||||||
|
|
||||||
|
const l2 = {
|
||||||
|
bids: [{ price: bestBidPrice, size: new BN(1) }],
|
||||||
|
asks: [{ price: bestAskPrice, size: new BN(1) }],
|
||||||
|
} as any;
|
||||||
|
|
||||||
|
const worstPrice = new BN(100010000); // $100.01 in PRICE_PRECISION
|
||||||
|
|
||||||
|
const slippage = calculateDynamicSlippage(
|
||||||
|
'long',
|
||||||
|
0,
|
||||||
|
'perp',
|
||||||
|
mockDriftClient,
|
||||||
|
l2,
|
||||||
|
startPrice,
|
||||||
|
worstPrice
|
||||||
|
);
|
||||||
|
|
||||||
|
// Should respect the 2% minimum
|
||||||
|
expect(slippage).toBeGreaterThanOrEqual(2.0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -1172,6 +1172,7 @@ export const mapToMarketOrderParams = async (
|
|||||||
const startPrice = estimatedPrices[startPriceProperty];
|
const startPrice = estimatedPrices[startPriceProperty];
|
||||||
|
|
||||||
processedSlippageTolerance = calculateDynamicSlippage(
|
processedSlippageTolerance = calculateDynamicSlippage(
|
||||||
|
direction === PositionDirection.LONG ? 'long' : 'short',
|
||||||
params.marketIndex,
|
params.marketIndex,
|
||||||
params.marketType,
|
params.marketType,
|
||||||
driftClient,
|
driftClient,
|
||||||
@@ -1377,6 +1378,7 @@ export const fetchL2FromRedis = async (
|
|||||||
* @returns Dynamic slippage tolerance as a number
|
* @returns Dynamic slippage tolerance as a number
|
||||||
*/
|
*/
|
||||||
export const calculateDynamicSlippage = (
|
export const calculateDynamicSlippage = (
|
||||||
|
direction: 'long' | 'short',
|
||||||
marketIndex: number,
|
marketIndex: number,
|
||||||
marketType: string,
|
marketType: string,
|
||||||
driftClient: DriftClient,
|
driftClient: DriftClient,
|
||||||
@@ -1397,7 +1399,6 @@ export const calculateDynamicSlippage = (
|
|||||||
|
|
||||||
// Calculate spread using L2 data
|
// Calculate spread using L2 data
|
||||||
let spreadBaseSlippage = 0.0005; // 0.05% fallback spread
|
let spreadBaseSlippage = 0.0005; // 0.05% fallback spread
|
||||||
try {
|
|
||||||
// Get oracle data
|
// Get oracle data
|
||||||
const oracleData = isPerp
|
const oracleData = isPerp
|
||||||
? driftClient.getMMOracleDataForPerpMarket(marketIndex)
|
? driftClient.getMMOracleDataForPerpMarket(marketIndex)
|
||||||
@@ -1412,6 +1413,8 @@ export const calculateDynamicSlippage = (
|
|||||||
oraclePrice
|
oraclePrice
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
const spreadPctNum = BigNum.from(
|
const spreadPctNum = BigNum.from(
|
||||||
spreadInfo.spreadPct,
|
spreadInfo.spreadPct,
|
||||||
PERCENTAGE_PRECISION_EXP
|
PERCENTAGE_PRECISION_EXP
|
||||||
@@ -1464,7 +1467,39 @@ export const calculateDynamicSlippage = (
|
|||||||
const minSlippage = parseFloat(process.env.DYNAMIC_SLIPPAGE_MIN || '0.035'); // 0.035% minimum
|
const minSlippage = parseFloat(process.env.DYNAMIC_SLIPPAGE_MIN || '0.035'); // 0.035% minimum
|
||||||
const maxSlippage = parseFloat(process.env.DYNAMIC_SLIPPAGE_MAX || '5'); // 5% maximum
|
const maxSlippage = parseFloat(process.env.DYNAMIC_SLIPPAGE_MAX || '5'); // 5% maximum
|
||||||
|
|
||||||
return Math.min(Math.max(dynamicSlippage, minSlippage), maxSlippage);
|
let finalSlippage = Math.min(Math.max(dynamicSlippage, minSlippage), maxSlippage);
|
||||||
|
|
||||||
|
// make sure the slippage goes at least 1bp past the bestBid/bestAsk
|
||||||
|
const impliedEndPriceNum = direction === 'long' ? (startPrice.toNumber() * (1 + finalSlippage / 100)) : (startPrice.toNumber() * (1 - finalSlippage / 100));
|
||||||
|
|
||||||
|
const impliedEndPrice = new BN(impliedEndPriceNum);
|
||||||
|
|
||||||
|
// Adjust slippage to ensure it goes 1bp past best bid/ask if needed
|
||||||
|
try {
|
||||||
|
const ONE_BP = 0.0001; // 1 basis point = 0.01%
|
||||||
|
|
||||||
|
if (direction === 'long' && spreadInfo.bestAskPrice) {
|
||||||
|
// For LONG: if impliedEndPrice < bestAskPrice, adjust so startPrice + finalSlippage is 1bp greater than bestAskPrice
|
||||||
|
if (impliedEndPrice.lt(spreadInfo.bestAskPrice)) {
|
||||||
|
// Calculate required slippage: ((bestAskPrice * (1 + 1bp)) / startPrice - 1) * 100
|
||||||
|
const targetPrice = spreadInfo.bestAskPrice.toNumber() * (1 + ONE_BP);
|
||||||
|
const requiredSlippagePct = ((targetPrice / startPrice.toNumber()) - 1) * 100;
|
||||||
|
finalSlippage = Math.max(finalSlippage, requiredSlippagePct);
|
||||||
|
}
|
||||||
|
} else if (direction === 'short' && spreadInfo.bestBidPrice) {
|
||||||
|
// For SHORT: if impliedEndPrice > bestBidPrice, adjust so startPrice - finalSlippage is 1bp less than bestBidPrice
|
||||||
|
if (impliedEndPrice.gt(spreadInfo.bestBidPrice)) {
|
||||||
|
// Calculate required slippage: (1 - (bestBidPrice * (1 - 1bp)) / startPrice) * 100
|
||||||
|
const targetPrice = spreadInfo.bestBidPrice.toNumber() * (1 - ONE_BP);
|
||||||
|
const requiredSlippagePct = (1 - (targetPrice / startPrice.toNumber())) * 100;
|
||||||
|
finalSlippage = Math.max(finalSlippage, requiredSlippagePct);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Failed to adjust slippage for best bid/ask:', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return finalSlippage;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user