Skip to content

Commit

Permalink
sdk: contract tier funding rate clamp (#785)
Browse files Browse the repository at this point in the history
* bigz/sdk-contract-tier-funding-rate-clamp

* add sdk test

* tweak

* CHANGELOG

---------

Co-authored-by: Chris Heaney <[email protected]>
  • Loading branch information
0xbigz and crispheaney committed Jan 5, 2024
1 parent 2863b8b commit 7396049
Show file tree
Hide file tree
Showing 3 changed files with 180 additions and 3 deletions.
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 @@ import {
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 @@ export async function calculateAllEstimatedFundingRate(
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 @@ export async function calculateAllEstimatedFundingRate(
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

0 comments on commit 7396049

Please sign in to comment.