code.vegaprotocol.io/vega@v0.79.0/core/execution/future/special_orders.go (about) 1 // Copyright (C) 2023 Gobalsky Labs Limited 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16 //lint:file-ignore U1000 Ignore unused functions 17 18 package future 19 20 import ( 21 "context" 22 "sort" 23 24 "code.vegaprotocol.io/vega/core/events" 25 "code.vegaprotocol.io/vega/core/execution/common" 26 "code.vegaprotocol.io/vega/core/metrics" 27 "code.vegaprotocol.io/vega/core/types" 28 "code.vegaprotocol.io/vega/libs/num" 29 "code.vegaprotocol.io/vega/logging" 30 ) 31 32 func (m *Market) repricePeggedOrders( 33 ctx context.Context, 34 changes uint8, 35 ) (parked []*types.Order, toSubmit []*types.Order) { 36 timer := metrics.NewTimeCounter(m.mkt.ID, "market", "repricePeggedOrders") 37 38 // Go through *all* of the pegged orders and remove from the order book 39 // NB: this is getting all of the pegged orders that are unparked in the order book AND all 40 // the parked pegged orders. 41 allPeggedIDs := m.matching.GetActivePeggedOrderIDs() 42 allPeggedIDs = append(allPeggedIDs, m.peggedOrders.GetParkedIDs()...) 43 for _, oid := range allPeggedIDs { 44 var ( 45 order *types.Order 46 err error 47 ) 48 if m.peggedOrders.IsParked(oid) { 49 order = m.peggedOrders.GetParkedByID(oid) 50 } else { 51 order, err = m.matching.GetOrderByID(oid) 52 if err != nil { 53 m.log.Panic("if order is not parked, it should be on the book", logging.OrderID(oid)) 54 } 55 } 56 if common.OrderReferenceCheck(*order).HasMoved(changes) { 57 // First if the order isn't parked, then 58 // we will just remove if from the orderbook 59 if order.Status != types.OrderStatusParked { 60 // Remove order if any volume remains, 61 // otherwise it's already been popped by the matching engine. 62 cancellation, err := m.matching.CancelOrder(order) 63 if cancellation == nil || err != nil { 64 m.log.Panic("Failure after cancel order from matching engine", 65 logging.Order(*order), 66 logging.Error(err)) 67 } 68 69 // Remove it from the party position 70 _ = m.position.UnregisterOrder(ctx, order) 71 } else { 72 // unpark before it's reparked next eventually 73 m.peggedOrders.Unpark(order.ID) 74 } 75 76 if price, err := m.getNewPeggedPrice(order); err != nil { 77 // Failed to reprice, we need to park again 78 order.UpdatedAt = m.timeService.GetTimeNow().UnixNano() 79 order.Status = types.OrderStatusParked 80 order.Price = num.UintZero() 81 order.OriginalPrice = nil 82 m.broker.Send(events.NewOrderEvent(ctx, order)) 83 parked = append(parked, order) 84 } else { 85 // Repriced so all good make sure status is correct 86 order.Price = price.Clone() 87 order.OriginalPrice, _ = num.UintFromDecimal(price.ToDecimal().Div(m.priceFactor)) 88 order.Status = types.OrderStatusActive 89 order.UpdatedAt = m.timeService.GetTimeNow().UnixNano() 90 toSubmit = append(toSubmit, order) 91 } 92 } 93 } 94 95 timer.EngineTimeCounterAdd() 96 97 return parked, toSubmit 98 } 99 100 func (m *Market) reSubmitPeggedOrders( 101 ctx context.Context, 102 toSubmitOrders []*types.Order, 103 ) ([]*types.Order, map[string]events.MarketPosition) { 104 var ( 105 partiesPos = map[string]events.MarketPosition{} 106 updatedOrders = []*types.Order{} 107 evts = []events.Event{} 108 ) 109 110 // Reinsert all the orders 111 for _, order := range toSubmitOrders { 112 m.matching.ReSubmitSpecialOrders(order) 113 partiesPos[order.Party] = m.position.RegisterOrder(ctx, order) 114 updatedOrders = append(updatedOrders, order) 115 evts = append(evts, events.NewOrderEvent(ctx, order)) 116 } 117 118 // send new order events 119 m.broker.SendBatch(evts) 120 121 return updatedOrders, partiesPos 122 } 123 124 func (m *Market) repriceAllSpecialOrders( 125 ctx context.Context, 126 changes uint8, 127 orderUpdates []*types.Order, 128 ) []*types.Order { 129 if changes == 0 && len(orderUpdates) <= 0 { 130 // nothing to do, prices didn't move, 131 // no orders have been updated, there's no 132 // reason pegged order should get repriced 133 return nil 134 } 135 136 // first we get all the pegged orders to be resubmitted with a new price 137 var parked, toSubmit []*types.Order 138 if changes != 0 { 139 parked, toSubmit = m.repricePeggedOrders(ctx, changes) 140 for _, topark := range parked { 141 m.peggedOrders.Park(topark) 142 } 143 } 144 145 needsPeggedUpdates := len(parked) > 0 || len(toSubmit) > 0 146 147 if !needsPeggedUpdates && len(toSubmit) < 1 { 148 return nil 149 } 150 151 updatedOrders, partiesPos := m.reSubmitPeggedOrders(ctx, toSubmit) 152 risks, _, _ := m.updateMargins(ctx, partiesPos) 153 if len(risks) > 0 { 154 transfers, distressed, _, err := m.collateral.MarginUpdate( 155 ctx, m.GetID(), risks) 156 if err == nil && len(transfers) > 0 { 157 evt := events.NewLedgerMovements(ctx, transfers) 158 m.broker.Send(evt) 159 } 160 for _, p := range distressed { 161 distressedParty := p.Party() 162 for _, o := range updatedOrders { 163 if o.Party == distressedParty && o.Status == types.OrderStatusActive { 164 // cancel only the pegged orders, the reset will get picked up during regular closeout flow if need be 165 _, err := m.cancelOrder(ctx, distressedParty, o.ID) 166 if err != nil { 167 m.log.Panic("Failed to cancel order", 168 logging.Error(err), 169 logging.String("OrderID", o.ID)) 170 } 171 } 172 } 173 } 174 } 175 176 return updatedOrders 177 } 178 179 func (m *Market) updateMargins(ctx context.Context, partiesPos map[string]events.MarketPosition) ([]events.Risk, []events.MarketPosition, map[string]*num.Uint) { 180 // an ordered list of positions 181 var ( 182 positions = make([]events.MarketPosition, 0, len(partiesPos)) 183 marginsBefore = map[string]*num.Uint{} 184 id = m.GetID() 185 ) 186 // now we can check parties positions 187 for party, pos := range partiesPos { 188 if m.getMarginMode(pos.Party()) == types.MarginModeIsolatedMargin { 189 continue 190 } 191 positions = append(positions, pos) 192 mar, err := m.collateral.GetPartyMarginAccount(id, party, m.settlementAsset) 193 if err != nil { 194 m.log.Panic("party have position without a margin", 195 logging.MarketID(id), 196 logging.PartyID(party), 197 ) 198 } 199 marginsBefore[party] = mar.Balance 200 } 201 202 sort.Slice(positions, func(i, j int) bool { 203 return positions[i].Party() < positions[j].Party() 204 }) 205 206 // now we calculate all the new margins 207 return m.updateMargin(ctx, positions), positions, marginsBefore 208 } 209 210 func (m *Market) enterAuctionSpecialOrders(ctx context.Context) { 211 // First remove all GFN orders from the peg list. 212 ordersEvts := m.peggedOrders.EnterAuction(ctx) 213 m.broker.SendBatch(ordersEvts) 214 215 // Park all pegged orders 216 m.parkAllPeggedOrders(ctx) 217 }