Skip to content

Commit

Permalink
Check margin requirement for IOC orders in matching
Browse files Browse the repository at this point in the history
  • Loading branch information
lumos42 committed Dec 20, 2023
1 parent 036cc2e commit 6cb7491
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 9 deletions.
5 changes: 5 additions & 0 deletions plugin/evm/orderbook/config_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type IConfigService interface {
GetCumulativePremiumFraction(market Market) *big.Int
GetAcceptableBounds(market Market) (*big.Int, *big.Int)
GetAcceptableBoundsForLiquidation(market Market) (*big.Int, *big.Int)
GetTakerFee() *big.Int
}

type ConfigService struct {
Expand Down Expand Up @@ -102,3 +103,7 @@ func (cs *ConfigService) GetCumulativePremiumFraction(market Market) *big.Int {
markets := bibliophile.GetMarkets(cs.getStateAtCurrentBlock())
return bibliophile.GetCumulativePremiumFraction(cs.getStateAtCurrentBlock(), markets[market])
}

func (cs *ConfigService) GetTakerFee() *big.Int {
return bibliophile.GetTakerFee(cs.getStateAtCurrentBlock())
}
30 changes: 22 additions & 8 deletions plugin/evm/orderbook/contract_events_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,9 +155,10 @@ func (cep *ContractEventsProcessor) handleOrderBookEvent(event *types.Log) {
return
}
orderId := event.Topics[1]
errorString := args["err"].(string)
if !removed {
log.Info("OrderMatchingError", "args", args, "orderId", orderId.String(), "TxHash", event.TxHash, "number", event.BlockNumber)
if err := cep.database.SetOrderStatus(orderId, Execution_Failed, args["err"].(string), event.BlockNumber); err != nil {
if err := cep.database.SetOrderStatus(orderId, Execution_Failed, errorString, event.BlockNumber); err != nil {
log.Error("error in SetOrderStatus", "method", "OrderMatchingError", "err", err)
return
}
Expand Down Expand Up @@ -704,17 +705,30 @@ func (cep *ContractEventsProcessor) updateMetrics(logs []*types.Log) {

metricName := fmt.Sprintf("%s/%s", "events", event_.Name)

if !event.Removed {
metrics.GetOrRegisterCounter(metricName, nil).Inc(1)
} else {
metrics.GetOrRegisterCounter(metricName, nil).Dec(1)
}
metrics.GetOrRegisterCounter(metricName, nil).Inc(1)

switch event_.Name {
case "OrderAccepted":
orderAcceptedCount++
orderAcceptedCount += 1
case "OrderCancelAccepted":
orderCancelledCount++
orderCancelledCount += 1
case "OrderMatchingError":
// separate metrics for combination of order type and error string - for more granular analysis
args := map[string]interface{}{}
err := cep.orderBookABI.UnpackIntoMap(args, "OrderMatchingError", event.Data)
if err != nil {
log.Error("error in orderBookAbi.UnpackIntoMap", "method", "OrderMatchingError", "err", err)
return
}
orderId := event.Topics[1]
errorString := args["err"].(string)

order := cep.database.GetOrderById(orderId)
if order != nil {
ordertype := order.OrderType
metricName := fmt.Sprintf("%s/%s/%s/%s", "events", "OrderMatchingError", ordertype, utils.RemoveSpacesAndSpecialChars(errorString))
metrics.GetOrRegisterCounter(metricName, nil).Inc(1)
}
}
}

Expand Down
57 changes: 56 additions & 1 deletion plugin/evm/orderbook/matching_pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ func (pipeline *MatchingPipeline) Run(blockNumber *big.Int) bool {
cancellableOrderIds := pipeline.cancelLimitOrders(ordersToCancel)
orderMap := make(map[Market]*Orders)
for _, market := range markets {
orderMap[market] = pipeline.fetchOrders(market, hState.OraclePrices[market], cancellableOrderIds, blockNumber)
orders := pipeline.fetchOrders(market, hState.OraclePrices[market], cancellableOrderIds, blockNumber)
orderMap[market] = pipeline.filterEligibleOrders(orders, hState)
}
pipeline.runLiquidations(liquidablePositions, orderMap, hState.OraclePrices)
for _, market := range markets {
Expand Down Expand Up @@ -246,6 +247,7 @@ func (pipeline *MatchingPipeline) runLiquidations(liquidablePositions []Liquidab
orderMap[market].shortOrders = orderMap[market].shortOrders[numOrdersExhausted:]
}
if liquidable.GetUnfilledSize().Sign() != 0 {
unquenchedLiquidationsCounter.Inc(1)
log.Info("unquenched liquidation", "liquidable", liquidable)
}
}
Expand Down Expand Up @@ -275,6 +277,59 @@ func (pipeline *MatchingPipeline) runMatchingEngine(lotp LimitOrderTxProcessor,
}
}

func (pipeline *MatchingPipeline) filterEligibleOrders(orders *Orders, hState *hu.HubbleState) *Orders {

minAllowableMargin := pipeline.configService.getMinAllowableMargin()
takerFee := pipeline.configService.GetTakerFee()

// inner function for filtering
hasSufficientMargin := func(order Order) bool {
trader := pipeline.db.GetTraderInfo(order.Trader)
userState := hu.UserState{
Positions: translatePositions(trader.Positions),
Margins: getMargins(trader, len(hState.Assets)),
PendingFunding: getTotalFunding(trader, hState.ActiveMarkets),
ReservedMargin: new(big.Int).Set(trader.Margin.Reserved),
}
availableMargin := hu.GetAvailableMargin(hState, &userState)

price := order.Price
upperBound, _ := pipeline.configService.GetAcceptableBounds(order.Market)
if order.BaseAssetQuantity.Sign() == -1 && order.Price.Cmp(upperBound) == -1 {
price = upperBound
}
quoteAsset := big.NewInt(0).Abs(big.NewInt(0).Div(new(big.Int).Mul(order.BaseAssetQuantity, price), big.NewInt(1e18)))
requiredMargin := big.NewInt(0).Div(big.NewInt(0).Mul(minAllowableMargin, quoteAsset), big.NewInt(1e6))
takerFee := big.NewInt(0).Div(big.NewInt(0).Mul(quoteAsset, takerFee), big.NewInt(1e6))
requiredMargin.Add(requiredMargin, takerFee)

return availableMargin.Cmp(requiredMargin) != -1
}

filteredOrders := &Orders{
longOrders: []Order{},
shortOrders: []Order{},
}
for _, order := range orders.longOrders {
if order.OrderType == IOC {
if !hasSufficientMargin(order) {
continue
}
}
filteredOrders.longOrders = append(filteredOrders.longOrders, order)
}
for _, order := range orders.shortOrders {
if order.OrderType == IOC {
if !hasSufficientMargin(order) {
continue
}
}
filteredOrders.shortOrders = append(filteredOrders.shortOrders, order)
}

return filteredOrders
}

func areMatchingOrders(longOrder, shortOrder Order) *big.Int {
if longOrder.Price.Cmp(shortOrder.Price) == -1 {
return nil
Expand Down
3 changes: 3 additions & 0 deletions plugin/evm/orderbook/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,7 @@ var (

// order id not found while deleting
deleteOrderIdNotFoundCounter = metrics.NewRegisteredCounter("delete_order_id_not_found", nil)

// unquenched liquidations
unquenchedLiquidationsCounter = metrics.NewRegisteredCounter("unquenched_liquidations", nil)
)
15 changes: 15 additions & 0 deletions utils/string.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package utils

import (
"strings"
"unicode"
)

func ContainsString(list []string, item string) bool {
for _, i := range list {
if i == item {
Expand All @@ -8,3 +13,13 @@ func ContainsString(list []string, item string) bool {
}
return false
}

func RemoveSpacesAndSpecialChars(str string) string {
var builder strings.Builder
for _, r := range str {
if unicode.IsLetter(r) || unicode.IsNumber(r) {
builder.WriteRune(r)
}
}
return builder.String()
}

0 comments on commit 6cb7491

Please sign in to comment.