Skip to content

Commit

Permalink
going through tests
Browse files Browse the repository at this point in the history
  • Loading branch information
toteki committed Nov 8, 2023
1 parent 613d9a0 commit c08d072
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 13 deletions.
44 changes: 43 additions & 1 deletion x/leverage/types/position.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,12 +256,40 @@ func (ap *AccountPosition) Limit() sdk.Dec {
// compute limit due to collateral weights
limit := ap.totalBorrowLimit()

// if no borrows, borrow factor limits will not apply
borrowedValue := ap.BorrowedValue()
if borrowedValue.IsZero() {
return limit
}

// compute limit due to borrow factors
usage := ap.totalCollateralUsage()
avgWeight := ap.normalBorrowLimit().Quo(collateralValue)
unusedCollateralValue := ap.CollateralValue().Sub(usage) // can be negative

var avgWeight sdk.Dec
if unusedCollateralValue.IsPositive() {
// if user is below limit, unused collateral can be borrowed against at its average collateral weight at most
avgWeight = ap.averageWeight(ap.collateralValue)
} else {
// if user if above limit, overused collateral is being borrowed against at borrow factor
avgWeight = borrowedValue.Quo(usage)
}
borrowFactorLimit := ap.BorrowedValue().Add(unusedCollateralValue.Mul(avgWeight))

if len(ap.collateralValue) == 1 && ap.collateralValue[0].Denom == "FFFF" {
if len(ap.borrowedValue) == 1 && ap.borrowedValue[0].Denom == "HHHH" {
fmt.Printf("%s -> %s (%t)\n %s, %s\n >>> %s\n",
ap.collateralValue,
ap.borrowedValue,
ap.isForLiquidation,
limit,
borrowFactorLimit,
usage,
)
}
}

// return the minimum of the two limits
return sdk.MinDec(limit, borrowFactorLimit)
}

Expand Down Expand Up @@ -300,6 +328,20 @@ func (ap *AccountPosition) tokenWeight(denom string) sdk.Dec {
return sdk.ZeroDec()
}

// averageWeight gets the weighted average collateral weight (or liquidation threshold) of a set of tokens
func (ap *AccountPosition) averageWeight(coins sdk.DecCoins) sdk.Dec {
if coins.IsZero() {
return sdk.OneDec()
}
valueSum := sdk.ZeroDec()
weightSum := sdk.ZeroDec()
for _, c := range coins {
weightSum = weightSum.Add(c.Amount.Mul(ap.tokenWeight(c.Denom)))
valueSum = valueSum.Add(c.Amount)
}
return weightSum.Quo(valueSum)
}

// borrowFactor gets a token's collateral weight or liquidation threshold (or minimumBorrowFactor if greater)
// if the token is registered, else zero.
func (ap *AccountPosition) borrowFactor(denom string) sdk.Dec {
Expand Down
54 changes: 42 additions & 12 deletions x/leverage/types/position_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,17 +164,17 @@ func TestBorrowLimit(t *testing.T) {
{
// multiple assets, one with zero weight, at borrow limit
sdk.NewDecCoins(
coin.Dec("AAAA", "100"),
coin.Dec("GGGG", "100"),
coin.Dec("IIII", "100"),
coin.Dec("AAAA", "100"), // $10, $15
coin.Dec("GGGG", "100"), // $70, $75
coin.Dec("IIII", "100"), // $0, $95
),
sdk.NewDecCoins(
coin.Dec("GGGG", "80"),
coin.Dec("GGGG", "80"), // uses $114.2 or $106.6 of collateral
),
// effectiveness of I collateral is reduced to due to G liquidation threshold, thus leading
// to a lower liquidation threshold than "simple AGI" test case above
// effectiveness of I collateral would be reduced to due to G liquidation threshold,
// but ordinary liquidation threshold is already more restrictive than borrow factor here
"80.00",
"165.00",
"185.00",
"AGI -> G at borrow limit",
},
{
Expand All @@ -185,12 +185,12 @@ func TestBorrowLimit(t *testing.T) {
coin.Dec("IIII", "100"),
),
sdk.NewDecCoins(
coin.Dec("GGGG", "165"),
coin.Dec("GGGG", "185"),
),
// significantly over borrow limit, so calculation subtracts value of unpaired borrows
// from total borrowed value to determine borrow limit
// from total borrowed value to determine borrow limit to arrive at the same result
"80.00",
"165.00",
"185.00",
"AGI -> G at liquidation threshold",
},
{
Expand All @@ -206,7 +206,7 @@ func TestBorrowLimit(t *testing.T) {
// significantly over borrow limit and liquidation threshold, but calculation still reaches
// the same values for them
"80.00",
"165.00",
"185.00",
"AGI -> G above liquidation threshold",
},
{
Expand Down Expand Up @@ -250,6 +250,36 @@ func TestBorrowLimit(t *testing.T) {
"53.00",
"F -> A",
},
{
// single asset with unused special pair (borrowFactor reducing weight, minimumBorrowFactor, at limit)
sdk.NewDecCoins(
coin.Dec("FFFF", "100"),
),
sdk.NewDecCoins(
coin.Dec("AAAA", "50"),
),
// 50 A consumes 100 F collateral (weight 0.5 due to MinimumBorrowFactor)
// the F <-> H special pair has no effect
"50.00",
"50.00",
"F -> A",
},
{
// single asset with unused special pair (borrowFactor, minimumBorrowFactor, over limits)
sdk.NewDecCoins(
coin.Dec("FFFF", "100"),
),
sdk.NewDecCoins(
coin.Dec("AAAA", "80"),
),
// 80 A would consume 160 F collateral (weight 0.5 due to MinimumBorrowFactor),
// meanwhile 100F on its own would have 60, 65 borrow limit and liquidation threshold.
// The calculation works backwards from the 160/80 collateral usage to find the limit at 100
// the F <-> H special pair has no effect
"50.00",
"50.00",
"F -> A over limits",
},
{
// single asset with special pair in effect
sdk.NewDecCoins(
Expand Down Expand Up @@ -291,7 +321,7 @@ func TestBorrowLimit(t *testing.T) {
),
// 60 H consumes all 100 F collateral (weight 0.6 due to Special Pair).
// A remaining 20H is unpaired borrowed value. Borrow limit equals value minus unpaired.
// Meanwhile, 80A consumes 100 F collateral (liquidation threshold 0.8 due to special pair).
// Meanwhile, 80 H consumes 100 F collateral (liquidation threshold 0.8 due to special pair).
// Liquidation threshold is exactly borrowed value.
"60.00",
"80.00",
Expand Down

0 comments on commit c08d072

Please sign in to comment.