Skip to content

Commit

Permalink
Clean up open-interest liquidity tier calculations (#1592)
Browse files Browse the repository at this point in the history
  • Loading branch information
BrendanChou authored May 29, 2024
1 parent 0c67bc4 commit df00d84
Show file tree
Hide file tree
Showing 5 changed files with 280 additions and 256 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/protocol-benchmark.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ name: Protocol Benchmark
on: # yamllint disable-line rule:truthy
pull_request:
paths:
- 'protocol/lib/**'
- 'protocol/**'
push:
branches:
- main
- 'release/protocol/v[0-9]+.[0-9]+.x' # e.g. release/protocol/v0.1.x
- 'release/protocol/v[0-9]+.x' # e.g. release/protocol/v1.x
paths:
- 'protocol/lib/**'
- 'protocol/**'

concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
Expand Down
257 changes: 112 additions & 145 deletions protocol/x/perpetuals/keeper/perpetual_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -681,107 +681,107 @@ func TestModifyOpenInterest_Mixed(t *testing.T) {
func TestGetMarginRequirements_Success(t *testing.T) {
oneBip := math.Pow10(2)
tests := map[string]struct {
price uint64
exponent int32
baseCurrencyAtomicResolution int32
bigBaseQuantums *big.Int
initialMarginPpm uint32
maintenanceFractionPpm uint32
openInterest *big.Int
openInterestLowerCap uint64
openInterestUpperCap uint64
bigExpectedInitialMarginPpm *big.Int
bigExpectedMaintenanceMarginPpm *big.Int
price uint64
exponent int32
baseCurrencyAtomicResolution int32
bigBaseQuantums *big.Int
initialMarginPpm uint32
maintenanceFractionPpm uint32
openInterest *big.Int
openInterestLowerCap uint64
openInterestUpperCap uint64
bigExpectedInitialMargin *big.Int
bigExpectedMaintenanceMargin *big.Int
}{
"InitialMargin 2 BIPs, MaintenanceMargin 1 BIP, positive exponent, atomic resolution 8": {
price: 5_555,
exponent: 2,
baseCurrencyAtomicResolution: -8,
bigBaseQuantums: big.NewInt(7_000),
initialMarginPpm: uint32(oneBip * 2),
maintenanceFractionPpm: uint32(500_000), // 50% of IM
bigExpectedInitialMarginPpm: big.NewInt(7_777),
bigExpectedMaintenanceMarginPpm: big.NewInt(3_889),
price: 5_555,
exponent: 2,
baseCurrencyAtomicResolution: -8,
bigBaseQuantums: big.NewInt(7_000),
initialMarginPpm: uint32(oneBip * 2),
maintenanceFractionPpm: uint32(500_000), // 50% of IM
bigExpectedInitialMargin: big.NewInt(7_777),
bigExpectedMaintenanceMargin: big.NewInt(3_889),
},
"InitialMargin 100 BIPs, MaintenanceMargin 50 BIPs, atomic resolution 4": {
price: 5_555,
exponent: 0,
baseCurrencyAtomicResolution: -4,
bigBaseQuantums: big.NewInt(7_000),
initialMarginPpm: uint32(oneBip * 100),
maintenanceFractionPpm: uint32(500_000), // 50% of IM
bigExpectedInitialMarginPpm: big.NewInt(38_885_000),
bigExpectedMaintenanceMarginPpm: big.NewInt(19_442_500),
price: 5_555,
exponent: 0,
baseCurrencyAtomicResolution: -4,
bigBaseQuantums: big.NewInt(7_000),
initialMarginPpm: uint32(oneBip * 100),
maintenanceFractionPpm: uint32(500_000), // 50% of IM
bigExpectedInitialMargin: big.NewInt(38_885_000),
bigExpectedMaintenanceMargin: big.NewInt(19_442_500),
},
"InitialMargin 100 BIPs, MaintenanceMargin 50 BIPs, positive exponent, atomic resolution 0": {
price: 42,
exponent: 5,
baseCurrencyAtomicResolution: -0,
bigBaseQuantums: big.NewInt(88),
initialMarginPpm: uint32(oneBip * 100),
maintenanceFractionPpm: uint32(500_000), // 50% of IM
bigExpectedInitialMarginPpm: big.NewInt(3_696_000_000_000),
bigExpectedMaintenanceMarginPpm: big.NewInt(1_848_000_000_000),
price: 42,
exponent: 5,
baseCurrencyAtomicResolution: -0,
bigBaseQuantums: big.NewInt(88),
initialMarginPpm: uint32(oneBip * 100),
maintenanceFractionPpm: uint32(500_000), // 50% of IM
bigExpectedInitialMargin: big.NewInt(3_696_000_000_000),
bigExpectedMaintenanceMargin: big.NewInt(1_848_000_000_000),
},
"InitialMargin 100 BIPs, MaintenanceMargin 50 BIPs, negative exponent, atomic resolution 6": {
price: 42_000_000,
exponent: -2,
baseCurrencyAtomicResolution: -6,
bigBaseQuantums: big.NewInt(-5_000),
initialMarginPpm: uint32(oneBip * 100),
maintenanceFractionPpm: uint32(500_000), // 50% of IM
bigExpectedInitialMarginPpm: big.NewInt(21_000_000),
bigExpectedMaintenanceMarginPpm: big.NewInt(10_500_000),
price: 42_000_000,
exponent: -2,
baseCurrencyAtomicResolution: -6,
bigBaseQuantums: big.NewInt(-5_000),
initialMarginPpm: uint32(oneBip * 100),
maintenanceFractionPpm: uint32(500_000), // 50% of IM
bigExpectedInitialMargin: big.NewInt(21_000_000),
bigExpectedMaintenanceMargin: big.NewInt(10_500_000),
},
"InitialMargin 10_000 BIPs (max), MaintenanceMargin 10_000 BIPs (max), atomic resolution 6": {
price: 5_555,
exponent: 0,
baseCurrencyAtomicResolution: -6,
bigBaseQuantums: big.NewInt(7_000),
initialMarginPpm: uint32(oneBip * 10_000),
maintenanceFractionPpm: uint32(1_000_000), // 100% of IM
bigExpectedInitialMarginPpm: big.NewInt(38_885_000),
bigExpectedMaintenanceMarginPpm: big.NewInt(38_885_000),
price: 5_555,
exponent: 0,
baseCurrencyAtomicResolution: -6,
bigBaseQuantums: big.NewInt(7_000),
initialMarginPpm: uint32(oneBip * 10_000),
maintenanceFractionPpm: uint32(1_000_000), // 100% of IM
bigExpectedInitialMargin: big.NewInt(38_885_000),
bigExpectedMaintenanceMargin: big.NewInt(38_885_000),
},
"InitialMargin 100 BIPs, MaintenanceMargin 100 BIPs, atomic resolution 6": {
price: 5_555,
exponent: 0,
baseCurrencyAtomicResolution: -6,
bigBaseQuantums: big.NewInt(7_000),
initialMarginPpm: uint32(oneBip * 100),
maintenanceFractionPpm: uint32(1_000_000), // 100% of IM
bigExpectedInitialMarginPpm: big.NewInt(388_850),
bigExpectedMaintenanceMarginPpm: big.NewInt(388_850),
price: 5_555,
exponent: 0,
baseCurrencyAtomicResolution: -6,
bigBaseQuantums: big.NewInt(7_000),
initialMarginPpm: uint32(oneBip * 100),
maintenanceFractionPpm: uint32(1_000_000), // 100% of IM
bigExpectedInitialMargin: big.NewInt(388_850),
bigExpectedMaintenanceMargin: big.NewInt(388_850),
},
"InitialMargin 0.02 BIPs, MaintenanceMargin 0.01 BIPs, positive exponent, atomic resolution 6": {
price: 5_555,
exponent: 3,
baseCurrencyAtomicResolution: -6,
bigBaseQuantums: big.NewInt(-7_000),
initialMarginPpm: uint32(oneBip * 0.02),
maintenanceFractionPpm: uint32(500_000), // 50% of IM
bigExpectedInitialMarginPpm: big.NewInt(77_770),
bigExpectedMaintenanceMarginPpm: big.NewInt(38_885),
price: 5_555,
exponent: 3,
baseCurrencyAtomicResolution: -6,
bigBaseQuantums: big.NewInt(-7_000),
initialMarginPpm: uint32(oneBip * 0.02),
maintenanceFractionPpm: uint32(500_000), // 50% of IM
bigExpectedInitialMargin: big.NewInt(77_770),
bigExpectedMaintenanceMargin: big.NewInt(38_885),
},
"InitialMargin 0 BIPs (min), MaintenanceMargin 0 BIPs (min), atomic resolution 6": {
price: 5_555,
exponent: 0,
baseCurrencyAtomicResolution: -6,
bigBaseQuantums: big.NewInt(7_000),
initialMarginPpm: uint32(oneBip * 0),
maintenanceFractionPpm: uint32(1_000_000), // 100% of IM,
bigExpectedInitialMarginPpm: big.NewInt(0),
bigExpectedMaintenanceMarginPpm: big.NewInt(0),
price: 5_555,
exponent: 0,
baseCurrencyAtomicResolution: -6,
bigBaseQuantums: big.NewInt(7_000),
initialMarginPpm: uint32(oneBip * 0),
maintenanceFractionPpm: uint32(1_000_000), // 100% of IM,
bigExpectedInitialMargin: big.NewInt(0),
bigExpectedMaintenanceMargin: big.NewInt(0),
},
"Price is zero, atomic resolution 6": {
price: 0,
exponent: 1,
baseCurrencyAtomicResolution: -6,
bigBaseQuantums: big.NewInt(-7_000),
initialMarginPpm: uint32(oneBip * 1),
maintenanceFractionPpm: uint32(1_000_000), // 100% of IM,
bigExpectedInitialMarginPpm: big.NewInt(0),
bigExpectedMaintenanceMarginPpm: big.NewInt(0),
price: 0,
exponent: 1,
baseCurrencyAtomicResolution: -6,
bigBaseQuantums: big.NewInt(-7_000),
initialMarginPpm: uint32(oneBip * 1),
maintenanceFractionPpm: uint32(1_000_000), // 100% of IM,
bigExpectedInitialMargin: big.NewInt(0),
bigExpectedMaintenanceMargin: big.NewInt(0),
},
"Price and quantums are max uints": {
price: math.MaxUint64,
Expand All @@ -790,10 +790,10 @@ func TestGetMarginRequirements_Success(t *testing.T) {
bigBaseQuantums: new(big.Int).SetUint64(math.MaxUint64),
initialMarginPpm: uint32(oneBip * 1),
maintenanceFractionPpm: uint32(1_000_000), // 100% of IM,
bigExpectedInitialMarginPpm: big_testutil.MustFirst(
bigExpectedInitialMargin: big_testutil.MustFirst(
new(big.Int).SetString("340282366920938463426481119284349109", 10),
),
bigExpectedMaintenanceMarginPpm: big_testutil.MustFirst(
bigExpectedMaintenanceMargin: big_testutil.MustFirst(
new(big.Int).SetString("340282366920938463426481119284349109", 10),
),
},
Expand All @@ -806,8 +806,8 @@ func TestGetMarginRequirements_Success(t *testing.T) {
maintenanceFractionPpm: uint32(500_000), // 50% of IM
// initialMarginPpmQuoteQuantums = initialMarginPpm * quoteQuantums / 1_000_000
// = 10_000 * 38_885_000 / 1_000_000 ~= 388_850.
bigExpectedInitialMarginPpm: big.NewInt(388_850),
bigExpectedMaintenanceMarginPpm: big.NewInt(388_850 / 2),
bigExpectedInitialMargin: big.NewInt(388_850),
bigExpectedMaintenanceMargin: big.NewInt(388_850 / 2),
},
"InitialMargin 20%, MaintenanceMargin 10%, atomic resolution 6": {
price: 36_750,
Expand All @@ -819,8 +819,8 @@ func TestGetMarginRequirements_Success(t *testing.T) {
// quoteQuantums = 36_750 * 12_000 = 441_000_000
// initialMarginPpmQuoteQuantums = initialMarginPpm * quoteQuantums / 1_000_000
// = 200_000 * 441_000_000 / 1_000_000 ~= 88_200_000
bigExpectedInitialMarginPpm: big.NewInt(88_200_000),
bigExpectedMaintenanceMarginPpm: big.NewInt(88_200_000 / 2),
bigExpectedInitialMargin: big.NewInt(88_200_000),
bigExpectedMaintenanceMargin: big.NewInt(88_200_000 / 2),
},
"InitialMargin 5%, MaintenanceMargin 3%, atomic resolution 6": {
price: 123_456,
Expand All @@ -832,8 +832,8 @@ func TestGetMarginRequirements_Success(t *testing.T) {
// quoteQuantums = 123_456 * 74_523 = 9_200_311_488
// initialMarginPpmQuoteQuantums = initialMarginPpm * quoteQuantums / 1_000_000
// = 50_000 * 9_200_311_488 / 1_000_000 ~= 460_015_575
bigExpectedInitialMarginPpm: big.NewInt(460_015_575),
bigExpectedMaintenanceMarginPpm: big.NewInt(276_009_345),
bigExpectedInitialMargin: big.NewInt(460_015_575),
bigExpectedMaintenanceMargin: big.NewInt(276_009_345),
},
"InitialMargin 25%, MaintenanceMargin 15%, atomic resolution 6": {
price: 123_456,
Expand All @@ -843,8 +843,8 @@ func TestGetMarginRequirements_Success(t *testing.T) {
initialMarginPpm: uint32(250_000),
maintenanceFractionPpm: uint32(600_000), // 60% of IM
// quoteQuantums = 123_456 * 74_523 = 9_200_311_488
bigExpectedInitialMarginPpm: big.NewInt(2_300_077_872),
bigExpectedMaintenanceMarginPpm: big.NewInt(1_380_046_724), // Rounded up
bigExpectedInitialMargin: big.NewInt(2_300_077_872),
bigExpectedMaintenanceMargin: big.NewInt(1_380_046_724), // Rounded up
},
"OIMF: IM 20%, scaled to 60%, MaintenanceMargin 10%, atomic resolution 6": {
price: 36_750,
Expand All @@ -856,11 +856,12 @@ func TestGetMarginRequirements_Success(t *testing.T) {
openInterest: big.NewInt(408_163_265), // 408.163265
openInterestLowerCap: 10_000_000_000_000,
openInterestUpperCap: 20_000_000_000_000,
// quoteQuantums = 36_750 * 12_000 = 441_000_000
// initialMarginPpmQuoteQuantums = initialMarginPpm * quoteQuantums / 1_000_000
// = 200_000 * 441_000_000 / 1_000_000 ~= 88_200_000
bigExpectedInitialMarginPpm: big.NewInt(88_200_000 * 3),
bigExpectedMaintenanceMarginPpm: big.NewInt(88_200_000 / 2),
// openInterestNotional = 408_163_265 * 36_750 = 14_999_999_988_750
// percentageOfCap = (openInterestNotional - lowerCap) / (upperCap - lowerCap) = 0.499999998875
// adjustedIMF = (0.499999998875) * 0.8 + 0.2 = 0.5999999991 (rounded is 599_999 ppm)
// bigExpectedInitialMargin = bigBaseQuantums * price * adjustedIMF = 264_599_559
bigExpectedInitialMargin: big.NewInt(264_599_559),
bigExpectedMaintenanceMargin: big.NewInt(88_200_000 / 2),
},
"OIMF: IM 20%, scaled to 100%, MaintenanceMargin 10%, atomic resolution 6": {
price: 36_750,
Expand All @@ -875,8 +876,8 @@ func TestGetMarginRequirements_Success(t *testing.T) {
// quoteQuantums = 36_750 * 12_000 = 441_000_000
// initialMarginPpmQuoteQuantums = initialMarginPpm * quoteQuantums / 1_000_000
// = 200_000 * 441_000_000 / 1_000_000 ~= 88_200_000
bigExpectedInitialMarginPpm: big.NewInt(441_000_000),
bigExpectedMaintenanceMarginPpm: big.NewInt(88_200_000 / 2),
bigExpectedInitialMargin: big.NewInt(441_000_000),
bigExpectedMaintenanceMargin: big.NewInt(88_200_000 / 2),
},
"OIMF: IM 20%, lower_cap < realistic open interest < upper_cap, MaintenanceMargin 10%, atomic resolution 6": {
price: 36_750,
Expand All @@ -888,12 +889,12 @@ func TestGetMarginRequirements_Success(t *testing.T) {
openInterest: big.NewInt(1_123_456_789), // 1123.456 or ~$41mm notional
openInterestLowerCap: 25_000_000_000_000,
openInterestUpperCap: 50_000_000_000_000,
// quoteQuantums = 36_750 * 12_000 = 441_000_000
// initialMarginPpmQuoteQuantums = initialMarginPpm * quoteQuantums / 1_000_000
// = ((1123.456789 * 36750 - 25000000) / 25000000 * 0.8 + 0.2) * 441_000_000
// ~= 318042667
bigExpectedInitialMarginPpm: big.NewInt(318_042_667),
bigExpectedMaintenanceMarginPpm: big.NewInt(88_200_000 / 2),
// openInterestNotional = 1_123_456_789 * 36_750 = 41_287_036_995_750
// percentageOfCap = (openInterestNotional - lowerCap) / (upperCap - lowerCap) = 0.65148147983
// adjustedIMF = (0.65148147983) * 0.8 + 0.2 = 0.721185183864 (rounded is 721_185 ppm)
// bigExpectedInitialMargin = bigBaseQuantums * price * adjustedIMF = 318_042_585
bigExpectedInitialMargin: big.NewInt(318_042_585),
bigExpectedMaintenanceMargin: big.NewInt(88_200_000 / 2),
},
}

Expand Down Expand Up @@ -976,21 +977,8 @@ func TestGetMarginRequirements_Success(t *testing.T) {
)
require.NoError(t, err)

if tc.bigExpectedInitialMarginPpm.Cmp(bigInitialMargin) != 0 {
t.Fatalf(
"%s: expectedInitialMargin: %s, initialMargin: %s",
name,
tc.bigExpectedInitialMarginPpm.String(),
bigInitialMargin.String())
}

if tc.bigExpectedMaintenanceMarginPpm.Cmp(bigMaintenanceMargin) != 0 {
t.Fatalf(
"%s: expectedMaintenanceMargin: %s, maintenanceMargin: %s",
name,
tc.bigExpectedMaintenanceMarginPpm.String(),
bigMaintenanceMargin.String())
}
require.Equal(t, tc.bigExpectedInitialMargin, bigInitialMargin, "Initial margin mismatch")
require.Equal(t, tc.bigExpectedMaintenanceMargin, bigMaintenanceMargin, "Maintenance margin mismatch")
})
}
}
Expand Down Expand Up @@ -1177,14 +1165,7 @@ func TestGetNetNotional_Success(t *testing.T) {
)
require.NoError(t, err)

if tc.bigExpectedNetNotionalQuoteQuantums.Cmp(bigNotionalQuoteQuantums) != 0 {
t.Fatalf(
"%s: expectedNetNotionalQuoteQuantums: %s, collateralQuoteQuantums: %s",
name,
tc.bigExpectedNetNotionalQuoteQuantums.String(),
bigNotionalQuoteQuantums.String(),
)
}
require.Equal(t, tc.bigExpectedNetNotionalQuoteQuantums, bigNotionalQuoteQuantums, "Net notional mismatch")
})
}
}
Expand Down Expand Up @@ -1339,14 +1320,7 @@ func TestGetNotionalInBaseQuantums_Success(t *testing.T) {
)
require.NoError(t, err)

if tc.bigExpectedNetNotionalBaseQuantums.Cmp(bigNotionalBaseQuantums) != 0 {
t.Fatalf(
"%s: expectedNetNotionalBaseQuantums: %s, collateralBaseQuantums: %s",
name,
tc.bigExpectedNetNotionalBaseQuantums.String(),
bigNotionalBaseQuantums.String(),
)
}
require.Equal(t, tc.bigExpectedNetNotionalBaseQuantums, bigNotionalBaseQuantums, "Net notional mismatch")
})
}
}
Expand Down Expand Up @@ -1502,14 +1476,7 @@ func TestGetNetCollateral_Success(t *testing.T) {
)
require.NoError(t, err)

if tc.bigExpectedNetCollateralQuoteQuantums.Cmp(bigCollateralQuoteQuantums) != 0 {
t.Fatalf(
"%s: expectedNetCollateralQuoteQuantums: %s, collateralQuoteQuantums: %s",
name,
tc.bigExpectedNetCollateralQuoteQuantums.String(),
bigCollateralQuoteQuantums.String(),
)
}
require.Equal(t, tc.bigExpectedNetCollateralQuoteQuantums, bigCollateralQuoteQuantums, "Net collateral mismatch")
})
}
}
Expand Down
Loading

0 comments on commit df00d84

Please sign in to comment.