From 2f01bd6b4c0257610c4bdc9241901fd2505d3977 Mon Sep 17 00:00:00 2001 From: ChickenWhisky Date: Mon, 20 Jan 2025 02:44:47 +0530 Subject: [PATCH 1/3] Added a new field to the struct which is responsible for sending errors across goroutines --- internals/SubEvents/subevents.go | 2 +- internals/handlers/handlers.go | 51 ++++++++++++++++++++----- internals/ledger/ledger.go | 2 +- internals/orderbook/orderbook.go | 64 +++++++++++++++++++++++++++++--- pkg/models/hashHeaps.go | 2 +- pkg/models/orders.go | 19 +++++----- 6 files changed, 114 insertions(+), 26 deletions(-) diff --git a/internals/SubEvents/subevents.go b/internals/SubEvents/subevents.go index 7df4dce..60040ba 100644 --- a/internals/SubEvents/subevents.go +++ b/internals/SubEvents/subevents.go @@ -46,7 +46,7 @@ func (s *SubEvent) SubmitOrder(o models.Order) error { log.Printf("SubEvent has expired") return errors.New("SubEvent has expired") } - s.OrderBook.PushOrderIntoQueue(o) + return nil } diff --git a/internals/handlers/handlers.go b/internals/handlers/handlers.go index c80acfa..7b2ea8f 100644 --- a/internals/handlers/handlers.go +++ b/internals/handlers/handlers.go @@ -55,14 +55,13 @@ func SetupRoutes(router *gin.Engine) { // Event endpoints router.POST("/admin/event", CreateEvent) // Create a new event - // IMPLEMENT EDITING AN EVENT router.PUT("/admin/event", ModifyEvent) // Modify an event router.DELETE("/admin/event") // Delete an event // SubEvent endpoints - router.POST("/admin/subevent", CreateSubevent) // Create a new subevent - router.PUT("/admin/subevent", ModifySubEvent) // Modify a subevent - router.DELETE("/admin/subevent") // Delete a subevent + router.POST("/admin/subevent", CreateSubevent) // Create a new subevent + router.PUT("/admin/subevent", ModifySubEvent) // Modify a subevent + router.DELETE("/admin/subevent") // Delete a subevent } @@ -109,7 +108,7 @@ func CreateSubevent(c *gin.Context) { } event.AddSubevent([]subevents.SubEvent{*subevents.NewSubEvent("", tempSubEvent.SubEventName, time.Now(), false, subEventExpiry)}) - c.JSON(http.StatusOK, gin.H{"message": "SubEvent created successfully","subevent_name": tempSubEvent.SubEventName}) + c.JSON(http.StatusOK, gin.H{"message": "SubEvent created successfully", "subevent_name": tempSubEvent.SubEventName}) } // ModifySubEvent handles modifying an existing subevent such as expiry date, name, info @@ -337,15 +336,21 @@ func CreateOrder(c *gin.Context) { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } + Order.ErrChan = make(chan error,1) Order.SetRequestType("add") Order.SetTimestamp(time.Now().UnixMilli()) - err := l.SubmitOrder(Order) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } - + + err = <-Order.ErrChan + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + c.JSON(http.StatusOK, gin.H{"message": "Order created successfully", "Order": Order}) } @@ -365,6 +370,8 @@ func CancelOrder(c *gin.Context) { // "subevent_id": "SubEventID", // } var OrderForCancellation models.Order + OrderForCancellation.ErrChan = make(chan error,1) + if err := c.BindJSON(&OrderForCancellation); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return @@ -375,6 +382,12 @@ func CancelOrder(c *gin.Context) { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } + err = <-OrderForCancellation.ErrChan + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + c.JSON(http.StatusOK, gin.H{"message": "Order cancelled successfully"}) } @@ -399,6 +412,7 @@ func ModifyOrder(c *gin.Context) { // } var OrderForModification models.Order + OrderForModification.ErrChan = make(chan error,1) // Bind the input to the Order struct if err := c.BindJSON(&OrderForModification); err != nil { @@ -406,8 +420,7 @@ func ModifyOrder(c *gin.Context) { return } - OrderForModification.SetRequestType("modify") - + OrderForModification.SetRequestType("delete") // Submit the modified order err := l.SubmitOrder(OrderForModification) @@ -417,6 +430,26 @@ func ModifyOrder(c *gin.Context) { return } + // Wait for the order to be deleted + err = <-OrderForModification.ErrChan + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + OrderForModification.SetRequestType("add") + err = l.SubmitOrder(OrderForModification) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + err = <-OrderForModification.ErrChan + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + // Return success message c.JSON(http.StatusOK, gin.H{"message": "Order modified successfully"}) } diff --git a/internals/ledger/ledger.go b/internals/ledger/ledger.go index 613b6e6..354d4fa 100644 --- a/internals/ledger/ledger.go +++ b/internals/ledger/ledger.go @@ -53,7 +53,7 @@ func (l *Ledger) SubmitOrder(o models.Order) error { log.Printf("Error in submitting order ") return errors.New("Event doesn't exist") } - + // Submit the order to the event err := event.SubmitOrder(o) if err != nil { diff --git a/internals/orderbook/orderbook.go b/internals/orderbook/orderbook.go index 05f40a0..9063e41 100644 --- a/internals/orderbook/orderbook.go +++ b/internals/orderbook/orderbook.go @@ -55,13 +55,16 @@ func (ob *OrderBook) StartProcessingTrades() { case "add": if err := ob.AddOrder(&Order); err != nil { log.Printf("Error adding Order: %v", err) + Order.ErrChan <- err } case "delete": if err := ob.CancelOrder(&Order); err != nil { - log.Printf("Error deleting Order: %v", err) + log.Printf("Error delPushOrderIntoQueueeting Order: %v", err) + Order.ErrChan <- err } default: log.Printf("Unknown request type: %s", Order.RequestType) + Order.ErrChan <- errors.New("unknown request type") } } }() @@ -77,18 +80,24 @@ func (ob *OrderBook) StartProcessingOrders() { case "add": if err := ob.AddOrder(&Order); err != nil { log.Printf("Error adding Order: %v", err) + Order.ErrChan <- err } case "delete": if err := ob.CancelOrder(&Order); err != nil { log.Printf("Error deleting Order: %v", err) + Order.ErrChan <- err } case "modify": if err := ob.ModifyOrder(&Order); err != nil { log.Printf("Error modifying Order: %v", err) + Order.ErrChan <- err } default: log.Printf("Unknown request type: %s", Order.RequestType) + Order.ErrChan <- errors.New("unknown request type") } + Order.ErrChan <- nil + close(Order.ErrChan) } }() } @@ -100,35 +109,36 @@ func (ob *OrderBook) PushOrderIntoQueue(Order models.Order) { // AddOrder adds a new Order (order) to the order book and attempts to match orders. func (ob *OrderBook) AddOrder(Order *models.Order) error { - //Order.OrderID = helpers.GenerateRandomString(helpers.ConvertStringToInt(os.Getenv("Order_ID_LENGTH"))) - newOrderID := strconv.Itoa(ob.OrderNo) Order.OrderID = newOrderID ob.OrderNo++ switch Order.OrderType { case "sell": - err := ob.Asks.Push(Order) if err != nil { log.Printf("Error pushing Order into Asks: %v", err) + return err } log.Printf("Pushed Order into Asks: %+v", Order) case "buy": err := ob.Bids.Push(Order) if err != nil { log.Printf("Error pushing Order into Bids: %v", err) + return err } log.Printf("Pushed Order into Bids: %+v", Order) case "limit_sell": err := ob.LimitAsks.Push(Order) if err != nil { log.Printf("Error pushing Order into LimitAsks: %v", err) + return err } log.Printf("Pushed Order into LimitAsks: %+v", Order) case "limit_buy": err := ob.LimitBids.Push(Order) if err != nil { log.Printf("Error pushing Order into LimitBids: %v", err) + return err } log.Printf("Pushed Order into LimitBids: %+v", Order) default: @@ -205,7 +215,6 @@ func (ob *OrderBook) CancelOrder(Order *models.Order) error { } - // ISSUE TO BE FIXED POTENTIAL SITUATION WHERE ORDER CANT BE DELETED BUT DUE TO IMPLEMENTATION ANOTHER ORDER IS ADDED // ModifyOrder cancels a specific user's Order and then adds a new Order based on the updated modifications. func (ob *OrderBook) ModifyOrder(Order *models.Order) error { @@ -214,31 +223,76 @@ func (ob *OrderBook) ModifyOrder(Order *models.Order) error { case "buy": c, err := ob.Bids.Find(Order.OrderID) if err != nil { + log.Printf("Error Finding Order: %v", err) + return err + } + err = ob.CancelOrder(c) + if err != nil { + log.Printf("Error Canceling Order: %v", err) return err } c.SetPrice(Order.GetPrice()) c.SetQuantity(Order.GetQuantity()) + err = ob.AddOrder(c) + if err != nil { + log.Printf("Error adding Order: %v", err) + return err + } + case "sell": c, err := ob.Asks.Find(Order.OrderID) if err != nil { + log.Printf("Error Finding Order: %v", err) + return err + } + err = ob.CancelOrder(c) + if err != nil { + log.Printf("Error Canceling Order: %v", err) return err } c.SetPrice(Order.GetPrice()) c.SetQuantity(Order.GetQuantity()) + err = ob.AddOrder(c) + if err != nil { + log.Printf("Error adding Order: %v", err) + return err + } case "limit_buy": c, err := ob.LimitBids.Find(Order.OrderID) if err != nil { + log.Printf("Error Finding Order: %v", err) + return err + } + err = ob.CancelOrder(c) + if err != nil { + log.Printf("Error Canceling Order: %v", err) return err } c.SetPrice(Order.GetPrice()) c.SetQuantity(Order.GetQuantity()) + err = ob.AddOrder(c) + if err != nil { + log.Printf("Error adding Order: %v", err) + return err + } case "limit_sell": c, err := ob.LimitAsks.Find(Order.OrderID) if err != nil { + log.Printf("Error Finding Order: %v", err) + return err + } + err = ob.CancelOrder(c) + if err != nil { + log.Printf("Error Canceling Order: %v", err) return err } c.SetPrice(Order.GetPrice()) c.SetQuantity(Order.GetQuantity()) + err = ob.AddOrder(c) + if err != nil { + log.Printf("Error adding Order: %v", err) + return err + } default: return errors.New("invalid order type") } diff --git a/pkg/models/hashHeaps.go b/pkg/models/hashHeaps.go index 56f9115..6c2961d 100644 --- a/pkg/models/hashHeaps.go +++ b/pkg/models/hashHeaps.go @@ -23,7 +23,7 @@ func PriceTimeBased(a, b interface{}) int { A := a.(*Order) B := b.(*Order) if A.GetPrice() == B.GetPrice() { - return utils.Int64Comparator(A.GetTimestamp(), B.GetTimestamp()) + return utils.Int64Comparator(B.GetTimestamp(),A.GetTimestamp() ) } else { if A.GetOrderType() == "buy" || A.GetOrderType() == "limit_buy" { if A.GetPrice() < B.GetPrice() { diff --git a/pkg/models/orders.go b/pkg/models/orders.go index 918c832..9e3a65e 100644 --- a/pkg/models/orders.go +++ b/pkg/models/orders.go @@ -2,15 +2,16 @@ package models // Order struct represents an order (buy/sell) type Order struct { - OrderID string `json:"order_id"` - EventID string `json:"event_id"` - SubEventID string `json:"subevent_id"` - UserID string `json:"user_id"` - RequestType string `json:"request_type"` - OrderType string `json:"order_type"` - Price float32 `json:"price" binding:"required,gt=0"` - Quantity int64 `json:"quantity" binding:"required,gte=1"` - Timestamp int64 `json:"timestamp"` + OrderID string `json:"order_id"` + EventID string `json:"event_id"` + SubEventID string `json:"subevent_id"` + UserID string `json:"user_id"` + RequestType string `json:"request_type"` + OrderType string `json:"order_type"` + Price float32 `json:"price" binding:"required,gt=0"` + Quantity int64 `json:"quantity" binding:"required,gte=1"` + Timestamp int64 `json:"timestamp"` + ErrChan chan error `json:"-"` } // NewOrder creates a new instance of Order From b0526101c4489b581c2f69fae5e92bea0968f742 Mon Sep 17 00:00:00 2001 From: ChickenWhisky Date: Mon, 20 Jan 2025 02:48:33 +0530 Subject: [PATCH 2/3] Changed order of 2 lines in trade logger file as it logged number of contracts traded as not max(bid,ask)-min(bid,ask) but not as min(bid) --- internals/orderbook/matchOrderHelpers.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internals/orderbook/matchOrderHelpers.go b/internals/orderbook/matchOrderHelpers.go index 15ae733..643c230 100644 --- a/internals/orderbook/matchOrderHelpers.go +++ b/internals/orderbook/matchOrderHelpers.go @@ -78,14 +78,14 @@ func (ob *OrderBook) MergeTopPrices() error { lowestAsk, _ := ob.Asks.Pop() highestBid, _ := ob.Bids.Top() NoOfOrders = lowestAsk.GetQuantity() - highestBid.SetQuantity(highestBid.GetQuantity() - lowestAsk.GetQuantity()) ob.LogHandler(lowestAsk, highestBid) + highestBid.SetQuantity(highestBid.GetQuantity() - lowestAsk.GetQuantity()) } else { lowestAsk, _ := ob.Asks.Top() highestBid, _ := ob.Bids.Pop() NoOfOrders = highestBid.GetQuantity() - lowestAsk.SetQuantity(lowestAsk.GetQuantity() - highestBid.GetQuantity()) ob.LogHandler(lowestAsk, highestBid) + lowestAsk.SetQuantity(lowestAsk.GetQuantity() - highestBid.GetQuantity()) } log.Printf("\nAsk_Price : %v\n Bid_Price : %v\n Orders : %v", _lowestAsk.GetPrice(), _highestBid.GetPrice(), NoOfOrders) } From 13e1e1531815d81661520827e725bb1c6efa74a6 Mon Sep 17 00:00:00 2001 From: ChickenWhisky Date: Mon, 20 Jan 2025 02:56:06 +0530 Subject: [PATCH 3/3] Made a fix in the hashheap implementation, hashheap forgot to remove orders from Order map post matching an order --- internals/orderbook/matchOrderHelpers.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internals/orderbook/matchOrderHelpers.go b/internals/orderbook/matchOrderHelpers.go index 643c230..e84e8cb 100644 --- a/internals/orderbook/matchOrderHelpers.go +++ b/internals/orderbook/matchOrderHelpers.go @@ -80,12 +80,14 @@ func (ob *OrderBook) MergeTopPrices() error { NoOfOrders = lowestAsk.GetQuantity() ob.LogHandler(lowestAsk, highestBid) highestBid.SetQuantity(highestBid.GetQuantity() - lowestAsk.GetQuantity()) + delete(ob.Orders, lowestAsk.GetOrderID()) } else { lowestAsk, _ := ob.Asks.Top() highestBid, _ := ob.Bids.Pop() NoOfOrders = highestBid.GetQuantity() ob.LogHandler(lowestAsk, highestBid) lowestAsk.SetQuantity(lowestAsk.GetQuantity() - highestBid.GetQuantity()) + delete(ob.Orders, highestBid.GetOrderID()) } log.Printf("\nAsk_Price : %v\n Bid_Price : %v\n Orders : %v", _lowestAsk.GetPrice(), _highestBid.GetPrice(), NoOfOrders) }