Skip to content

Commit

Permalink
Fill price (#1689)
Browse files Browse the repository at this point in the history
* Add more tests for fill price

* Refactor fillPrice

* Lint

* Better order of describe/ before blocks

* Keep the calcs in the method.
- not sure how to refactor it if I'm not suppose to break it out to methods 🤔
  • Loading branch information
0xjocke authored Jun 29, 2023
1 parent fd6ceab commit 9fa0cde
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 19 deletions.
23 changes: 13 additions & 10 deletions markets/perps-market/contracts/storage/AsyncOrder.sol
Original file line number Diff line number Diff line change
Expand Up @@ -285,22 +285,12 @@ library AsyncOrder {
return takerFee + makerFee;
}

// TODO: refactor possibly
function calculateFillPrice(
int skew,
uint skewScale,
int size,
uint price
) internal pure returns (uint) {
if (skewScale == 0) {
return price;
}

int pdBefore = skew.divDecimal(skewScale.toInt());
int pdAfter = (skew + size).divDecimal(skewScale.toInt());
int priceBefore = price.toInt() + (price.toInt().mulDecimal(pdBefore));
int priceAfter = price.toInt() + (price.toInt().mulDecimal(pdAfter));

// How is the p/d-adjusted price calculated using an example:
//
// price = $1200 USD (oracle)
Expand Down Expand Up @@ -328,6 +318,19 @@ library AsyncOrder {
// fill_price = (price_before + price_after) / 2
// = (1200 + 1200.12) / 2
// = 1200.06
if (skewScale == 0) {
return price;
}
// calculate pd (premium/discount) before and after trade
int pdBefore = skew.divDecimal(skewScale.toInt());
int newSkew = skew + size;
int pdAfter = newSkew.divDecimal(skewScale.toInt());

// calculate price before and after trade with pd applied
int priceBefore = price.toInt() + (price.toInt().mulDecimal(pdBefore));
int priceAfter = price.toInt() + (price.toInt().mulDecimal(pdAfter));

// the fill price is the average of those prices
return (priceBefore + priceAfter).toUint().divDecimal(DecimalMath.UNIT * 2);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { ethers } from 'ethers';
import assertBn from '@synthetixio/core-utils/src/utils/assertions/assert-bignumber';
import { bn, bootstrapMarkets } from '../bootstrap';
import { OpenPositionData, openPosition } from '../helpers';
import { formatEther } from 'ethers/lib/utils';
import { snapshotCheckpoint } from '@synthetixio/core-utils/utils/mocha/snapshot';

describe('PerpsMarketModule', () => {
const fixture = {
Expand All @@ -10,7 +13,7 @@ describe('PerpsMarketModule', () => {
marketTokenPrice: bn(1000),
};

const { systems, perpsMarkets, restore } = bootstrapMarkets({
const { systems, perpsMarkets, marketOwner, provider, trader2, keeper } = bootstrapMarkets({
synthMarkets: [
{
name: 'Ether',
Expand Down Expand Up @@ -41,8 +44,6 @@ describe('PerpsMarketModule', () => {
});

describe('getMarketSummary', () => {
beforeEach(restore);

it('should return all values successfully', async () => {
const summary = await systems().PerpsMarket.getMarketSummary(marketId);
assertBn.equal(summary.skew, bn(0));
Expand All @@ -53,14 +54,89 @@ describe('PerpsMarketModule', () => {
assertBn.equal(summary.indexPrice, fixture.marketTokenPrice);
});
});

describe('fillPrice', () => {
it('should return correct value when passing same as onchain price', async () => {
const price = await systems().PerpsMarket.fillPrice(marketId, bn(1), bn(1000));
assertBn.equal(price, bn(1000.05));
let commonOpenPositionProps: Pick<
OpenPositionData,
| 'systems'
| 'provider'
| 'trader'
| 'accountId'
| 'keeper'
| 'marketId'
| 'settlementStrategyId'
>;
before('identify common props', async () => {
commonOpenPositionProps = {
systems,
provider,
marketId: marketId,
trader: trader2(),
accountId: 2,
keeper: keeper(),
settlementStrategyId: bn(0),
};
});

before('add collateral', async () => {
await systems().PerpsMarket.connect(trader2()).modifyCollateral(2, 0, bn(10000000));
});
describe('skewScale 0', () => {
const restoreSkewScale = snapshotCheckpoint(provider);
before('set skewScale to 0', async () => {
await systems().PerpsMarket.connect(marketOwner()).setFundingParameters(marketId, 0, 0);
});
it('should return the index price', async () => {
const price = await systems().PerpsMarket.fillPrice(marketId, bn(1), bn(1000));
assertBn.equal(price, fixture.marketTokenPrice);
});
after('restore skewScale', restoreSkewScale);
});
it('should return correct value when passing different price', async () => {
const price = await systems().PerpsMarket.fillPrice(marketId, bn(1), bn(1010));
assertBn.equal(price, bn(1010.0505));

const tests = [
{
marketSkew: 0,
sizeAndExpectedPrice: [
{ size: 1, price: bn(1010), expectedPrice: bn(1010.0505) },
{ size: -1, price: bn(1010), expectedPrice: bn(1009.9495) },
],
},
{
marketSkew: 10,
sizeAndExpectedPrice: [
{ size: 1, price: bn(1010), expectedPrice: bn(1011.0605) },
{ size: -1, price: bn(1010), expectedPrice: bn(1010.9595) },
{ size: -11, price: bn(1010), expectedPrice: bn(1010.4545) },
],
},
{
marketSkew: -10,
sizeAndExpectedPrice: [
{ size: 1, price: bn(1010), expectedPrice: bn(1009.0405) },
{ size: -1, price: bn(1010), expectedPrice: bn(1008.9395) },
{ size: 11, price: bn(1010), expectedPrice: bn(1009.5455) },
],
},
];
tests.forEach(({ marketSkew, sizeAndExpectedPrice }) => {
describe(`marketSkew ${marketSkew}`, () => {
const restoreMarketSkew = snapshotCheckpoint(provider);
before('create market skew', async () => {
if (marketSkew === 0) return;
await openPosition({
...commonOpenPositionProps,
sizeDelta: bn(marketSkew),
price: fixture.marketTokenPrice,
});
});
sizeAndExpectedPrice.forEach(({ size, price, expectedPrice }) => {
it(`fillPrice for size ${size} and price ${formatEther(price)}`, async () => {
const fillPrice = await systems().PerpsMarket.fillPrice(marketId, bn(size), price);
assertBn.equal(fillPrice, expectedPrice);
});
});
after('restore market skew', restoreMarketSkew);
});
});
});
});

0 comments on commit 9fa0cde

Please sign in to comment.