Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bigz/sdk contract tier funding rate clamp #785

Merged
merged 5 commits into from
Jan 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixes

- ts-sd: fix oracle is valid ([#806](https://github.com/drift-labs/protocol-v2/pull/806))
- ts-sdk: contract tier funding rate clamp ([#785](https://github.com/drift-labs/protocol-v2/pull/785))
- ts-sdk: fix oracle is valid ([#806](https://github.com/drift-labs/protocol-v2/pull/806))

### Breaking

Expand Down
31 changes: 29 additions & 2 deletions sdk/src/math/funding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@
ONE,
FUNDING_RATE_OFFSET_DENOMINATOR,
} from '../constants/numericConstants';
import { PerpMarketAccount, isVariant } from '../types';
import { ContractTier, PerpMarketAccount, isVariant } from '../types';

Check failure on line 10 in sdk/src/math/funding.ts

View workflow job for this annotation

GitHub Actions / yarn-lint

'ContractTier' is defined but never used. Allowed unused vars must match /^_/u
import { OraclePriceData } from '../oracles/types';
import { calculateBidAskPrice } from './amm';
import { calculateLiveOracleTwap } from './oracles';
import { clampBN } from './utils';

function calculateLiveMarkTwap(
market: PerpMarketAccount,
Expand Down Expand Up @@ -160,8 +161,15 @@
const twapSpreadWithOffset = twapSpread.add(
oracleTwap.abs().div(FUNDING_RATE_OFFSET_DENOMINATOR)
);
const maxSpread = getMaxPriceDivergenceForFundingRate(market, oracleTwap);

const twapSpreadPct = twapSpreadWithOffset
const clampedSpreadWithOffset = clampBN(
twapSpreadWithOffset,
maxSpread.mul(new BN(-1)),
maxSpread
);

const twapSpreadPct = clampedSpreadWithOffset
.mul(PRICE_PRECISION)
.mul(new BN(100))
.div(oracleTwap);
Expand Down Expand Up @@ -234,6 +242,25 @@
return [markTwap, oracleTwap, lowerboundEst, cappedAltEst, interpEst];
}

function getMaxPriceDivergenceForFundingRate(
market: PerpMarketAccount,
oracleTwap: BN
) {
if (isVariant(market.contractTier, 'a')) {
return oracleTwap.divn(33);
} else if (isVariant(market.contractTier, 'b')) {
return oracleTwap.divn(33);
} else if (isVariant(market.contractTier, 'c')) {
return oracleTwap.divn(20);
} else if (isVariant(market.contractTier, 'speculative')) {
return oracleTwap.divn(10);
} else if (isVariant(market.contractTier, 'isolated')) {
return oracleTwap.divn(10);
} else {
return oracleTwap.divn(10);
}
}

/**
*
* @param market
Expand Down
149 changes: 149 additions & 0 deletions sdk/tests/amm/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
squareRootBN,
calculateReferencePriceOffset,
calculateInventoryLiquidityRatio,
ContractTier,
isOracleValid,
OracleGuardRails,
} from '../../src';
Expand Down Expand Up @@ -1189,6 +1190,154 @@ describe('AMM Tests', () => {
assert(est2.eq(new BN('-719')));
});

it('predicted funding rate mock clamp', async () => {
const myMockPerpMarkets = _.cloneDeep(mockPerpMarkets);
const mockMarket1 = myMockPerpMarkets[0];

// make it like OP
const now = new BN(1688881915);

mockMarket1.amm.fundingPeriod = new BN(3600);
mockMarket1.amm.lastFundingRateTs = new BN(1688864415);

const currentMarkPrice = new BN(1.2242 * PRICE_PRECISION.toNumber()); // trading at a premium
const oraclePriceData: OraclePriceData = {
price: new BN(1.924 * PRICE_PRECISION.toNumber()),
slot: new BN(0),
confidence: new BN(1),
hasSufficientNumberOfDataPoints: true,
};
mockMarket1.amm.historicalOracleData.lastOraclePrice = new BN(
1.9535 * PRICE_PRECISION.toNumber()
);

// mockMarket1.amm.pegMultiplier = new BN(1.897573 * 1e3);

mockMarket1.amm.lastMarkPriceTwap = new BN(
1.218363 * PRICE_PRECISION.toNumber()
);
mockMarket1.amm.lastBidPriceTwap = new BN(
1.218363 * PRICE_PRECISION.toNumber()
);
mockMarket1.amm.lastAskPriceTwap = new BN(
1.218364 * PRICE_PRECISION.toNumber()
);
mockMarket1.amm.lastMarkPriceTwapTs = new BN(1688878815);

mockMarket1.amm.historicalOracleData.lastOraclePriceTwap = new BN(
1.820964 * PRICE_PRECISION.toNumber()
);
mockMarket1.amm.historicalOracleData.lastOraclePriceTwapTs = new BN(
1688879991
);
mockMarket1.contractTier = ContractTier.A;

const [
_markTwapLive,
_oracleTwapLive,
_lowerboundEst,
_cappedAltEst,
_interpEst,
] = await calculateAllEstimatedFundingRate(
mockMarket1,
oraclePriceData,
currentMarkPrice,
now
);

// console.log(_markTwapLive.toString());
// console.log(_oracleTwapLive.toString());
// console.log(_lowerboundEst.toString());
// console.log(_cappedAltEst.toString());
// console.log(_interpEst.toString());
// console.log('-----');

let [markTwapLive, oracleTwapLive, est1, est2] =
await calculateLongShortFundingRateAndLiveTwaps(
mockMarket1,
oraclePriceData,
currentMarkPrice,
now
);

console.log(
'markTwapLive:',
mockMarket1.amm.lastMarkPriceTwap.toString(),
'->',
markTwapLive.toString()
);
console.log(
'oracTwapLive:',
mockMarket1.amm.historicalOracleData.lastOraclePriceTwap.toString(),
'->',
oracleTwapLive.toString()
);
console.log('pred funding:', est1.toString(), est2.toString());

assert(markTwapLive.eq(new BN('1680634')));
assert(oracleTwapLive.eq(new BN('1876031')));
assert(est1.eq(est2));
assert(est2.eq(new BN('-126261')));

mockMarket1.contractTier = ContractTier.C;

[markTwapLive, oracleTwapLive, est1, est2] =
await calculateLongShortFundingRateAndLiveTwaps(
mockMarket1,
oraclePriceData,
currentMarkPrice,
now
);

console.log(
'markTwapLive:',
mockMarket1.amm.lastMarkPriceTwap.toString(),
'->',
markTwapLive.toString()
);
console.log(
'oracTwapLive:',
mockMarket1.amm.historicalOracleData.lastOraclePriceTwap.toString(),
'->',
oracleTwapLive.toString()
);
console.log('pred funding:', est1.toString(), est2.toString());

assert(markTwapLive.eq(new BN('1680634')));
assert(oracleTwapLive.eq(new BN('1876031')));
assert(est1.eq(est2));
assert(est2.eq(new BN('-208332')));

mockMarket1.contractTier = ContractTier.SPECULATIVE;

[markTwapLive, oracleTwapLive, est1, est2] =
await calculateLongShortFundingRateAndLiveTwaps(
mockMarket1,
oraclePriceData,
currentMarkPrice,
now
);

console.log(
'markTwapLive:',
mockMarket1.amm.lastMarkPriceTwap.toString(),
'->',
markTwapLive.toString()
);
console.log(
'oracTwapLive:',
mockMarket1.amm.historicalOracleData.lastOraclePriceTwap.toString(),
'->',
oracleTwapLive.toString()
);
console.log('pred funding:', est1.toString(), est2.toString());

assert(markTwapLive.eq(new BN('1680634')));
assert(oracleTwapLive.eq(new BN('1876031')));
assert(est1.eq(est2));
assert(est2.eq(new BN('-416666')));
});

it('orderbook L2 gen (no topOfBookQuoteAmounts, 10 numOrders, low liquidity)', async () => {
const myMockPerpMarkets = _.cloneDeep(mockPerpMarkets);

Expand Down
Loading