Skip to content

Commit 9fa0cde

Browse files
authored
Fill price (#1689)
* 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 🤔
1 parent fd6ceab commit 9fa0cde

File tree

2 files changed

+98
-19
lines changed

2 files changed

+98
-19
lines changed

markets/perps-market/contracts/storage/AsyncOrder.sol

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -285,22 +285,12 @@ library AsyncOrder {
285285
return takerFee + makerFee;
286286
}
287287

288-
// TODO: refactor possibly
289288
function calculateFillPrice(
290289
int skew,
291290
uint skewScale,
292291
int size,
293292
uint price
294293
) internal pure returns (uint) {
295-
if (skewScale == 0) {
296-
return price;
297-
}
298-
299-
int pdBefore = skew.divDecimal(skewScale.toInt());
300-
int pdAfter = (skew + size).divDecimal(skewScale.toInt());
301-
int priceBefore = price.toInt() + (price.toInt().mulDecimal(pdBefore));
302-
int priceAfter = price.toInt() + (price.toInt().mulDecimal(pdAfter));
303-
304294
// How is the p/d-adjusted price calculated using an example:
305295
//
306296
// price = $1200 USD (oracle)
@@ -328,6 +318,19 @@ library AsyncOrder {
328318
// fill_price = (price_before + price_after) / 2
329319
// = (1200 + 1200.12) / 2
330320
// = 1200.06
321+
if (skewScale == 0) {
322+
return price;
323+
}
324+
// calculate pd (premium/discount) before and after trade
325+
int pdBefore = skew.divDecimal(skewScale.toInt());
326+
int newSkew = skew + size;
327+
int pdAfter = newSkew.divDecimal(skewScale.toInt());
328+
329+
// calculate price before and after trade with pd applied
330+
int priceBefore = price.toInt() + (price.toInt().mulDecimal(pdBefore));
331+
int priceAfter = price.toInt() + (price.toInt().mulDecimal(pdAfter));
332+
333+
// the fill price is the average of those prices
331334
return (priceBefore + priceAfter).toUint().divDecimal(DecimalMath.UNIT * 2);
332335
}
333336
}

markets/perps-market/test/integration/Market/PerpsMarketModule.test.ts

Lines changed: 85 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { ethers } from 'ethers';
22
import assertBn from '@synthetixio/core-utils/src/utils/assertions/assert-bignumber';
33
import { bn, bootstrapMarkets } from '../bootstrap';
4+
import { OpenPositionData, openPosition } from '../helpers';
5+
import { formatEther } from 'ethers/lib/utils';
6+
import { snapshotCheckpoint } from '@synthetixio/core-utils/utils/mocha/snapshot';
47

58
describe('PerpsMarketModule', () => {
69
const fixture = {
@@ -10,7 +13,7 @@ describe('PerpsMarketModule', () => {
1013
marketTokenPrice: bn(1000),
1114
};
1215

13-
const { systems, perpsMarkets, restore } = bootstrapMarkets({
16+
const { systems, perpsMarkets, marketOwner, provider, trader2, keeper } = bootstrapMarkets({
1417
synthMarkets: [
1518
{
1619
name: 'Ether',
@@ -41,8 +44,6 @@ describe('PerpsMarketModule', () => {
4144
});
4245

4346
describe('getMarketSummary', () => {
44-
beforeEach(restore);
45-
4647
it('should return all values successfully', async () => {
4748
const summary = await systems().PerpsMarket.getMarketSummary(marketId);
4849
assertBn.equal(summary.skew, bn(0));
@@ -53,14 +54,89 @@ describe('PerpsMarketModule', () => {
5354
assertBn.equal(summary.indexPrice, fixture.marketTokenPrice);
5455
});
5556
});
57+
5658
describe('fillPrice', () => {
57-
it('should return correct value when passing same as onchain price', async () => {
58-
const price = await systems().PerpsMarket.fillPrice(marketId, bn(1), bn(1000));
59-
assertBn.equal(price, bn(1000.05));
59+
let commonOpenPositionProps: Pick<
60+
OpenPositionData,
61+
| 'systems'
62+
| 'provider'
63+
| 'trader'
64+
| 'accountId'
65+
| 'keeper'
66+
| 'marketId'
67+
| 'settlementStrategyId'
68+
>;
69+
before('identify common props', async () => {
70+
commonOpenPositionProps = {
71+
systems,
72+
provider,
73+
marketId: marketId,
74+
trader: trader2(),
75+
accountId: 2,
76+
keeper: keeper(),
77+
settlementStrategyId: bn(0),
78+
};
79+
});
80+
81+
before('add collateral', async () => {
82+
await systems().PerpsMarket.connect(trader2()).modifyCollateral(2, 0, bn(10000000));
83+
});
84+
describe('skewScale 0', () => {
85+
const restoreSkewScale = snapshotCheckpoint(provider);
86+
before('set skewScale to 0', async () => {
87+
await systems().PerpsMarket.connect(marketOwner()).setFundingParameters(marketId, 0, 0);
88+
});
89+
it('should return the index price', async () => {
90+
const price = await systems().PerpsMarket.fillPrice(marketId, bn(1), bn(1000));
91+
assertBn.equal(price, fixture.marketTokenPrice);
92+
});
93+
after('restore skewScale', restoreSkewScale);
6094
});
61-
it('should return correct value when passing different price', async () => {
62-
const price = await systems().PerpsMarket.fillPrice(marketId, bn(1), bn(1010));
63-
assertBn.equal(price, bn(1010.0505));
95+
96+
const tests = [
97+
{
98+
marketSkew: 0,
99+
sizeAndExpectedPrice: [
100+
{ size: 1, price: bn(1010), expectedPrice: bn(1010.0505) },
101+
{ size: -1, price: bn(1010), expectedPrice: bn(1009.9495) },
102+
],
103+
},
104+
{
105+
marketSkew: 10,
106+
sizeAndExpectedPrice: [
107+
{ size: 1, price: bn(1010), expectedPrice: bn(1011.0605) },
108+
{ size: -1, price: bn(1010), expectedPrice: bn(1010.9595) },
109+
{ size: -11, price: bn(1010), expectedPrice: bn(1010.4545) },
110+
],
111+
},
112+
{
113+
marketSkew: -10,
114+
sizeAndExpectedPrice: [
115+
{ size: 1, price: bn(1010), expectedPrice: bn(1009.0405) },
116+
{ size: -1, price: bn(1010), expectedPrice: bn(1008.9395) },
117+
{ size: 11, price: bn(1010), expectedPrice: bn(1009.5455) },
118+
],
119+
},
120+
];
121+
tests.forEach(({ marketSkew, sizeAndExpectedPrice }) => {
122+
describe(`marketSkew ${marketSkew}`, () => {
123+
const restoreMarketSkew = snapshotCheckpoint(provider);
124+
before('create market skew', async () => {
125+
if (marketSkew === 0) return;
126+
await openPosition({
127+
...commonOpenPositionProps,
128+
sizeDelta: bn(marketSkew),
129+
price: fixture.marketTokenPrice,
130+
});
131+
});
132+
sizeAndExpectedPrice.forEach(({ size, price, expectedPrice }) => {
133+
it(`fillPrice for size ${size} and price ${formatEther(price)}`, async () => {
134+
const fillPrice = await systems().PerpsMarket.fillPrice(marketId, bn(size), price);
135+
assertBn.equal(fillPrice, expectedPrice);
136+
});
137+
});
138+
after('restore market skew', restoreMarketSkew);
139+
});
64140
});
65141
});
66142
});

0 commit comments

Comments
 (0)