code.vegaprotocol.io/vega@v0.79.0/core/execution/spot/spot_execution_test.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 package spot_test 17 18 import ( 19 "context" 20 "testing" 21 "time" 22 23 "code.vegaprotocol.io/vega/core/execution/common" 24 "code.vegaprotocol.io/vega/core/idgeneration" 25 "code.vegaprotocol.io/vega/core/types" 26 vegacontext "code.vegaprotocol.io/vega/libs/context" 27 "code.vegaprotocol.io/vega/libs/crypto" 28 "code.vegaprotocol.io/vega/libs/num" 29 "code.vegaprotocol.io/vega/libs/ptr" 30 31 "github.com/stretchr/testify/require" 32 ) 33 34 func TestOpeningAuction(t *testing.T) { 35 now := time.Now() 36 ctx := context.Background() 37 ctx = vegacontext.WithTraceID(ctx, crypto.RandomHash()) 38 tm := newTestMarket(t, defaultPriceMonitorSettings, &types.AuctionDuration{Duration: 1}, now) 39 40 addAccountWithAmount(tm, "party1", 10, "BTC") 41 addAccountWithAmount(tm, "party1", 100000, "ETH") 42 addAccountWithAmount(tm, "party2", 5, "BTC") 43 addAccountWithAmount(tm, "party3", 500, "ETH") 44 addAccountWithAmount(tm, "party3", 2, "BTC") 45 addAccountWithAmount(tm, "party4", 10000, "ETH") 46 47 tm.market.StartOpeningAuction(ctx) 48 49 order1 := getGTCLimitOrder(tm, now, crypto.RandomHash(), types.SideBuy, "party1", 2, 30000) 50 tm.market.SubmitOrder(ctx, order1.IntoSubmission(), order1.Party, crypto.RandomHash()) 51 52 gaBalance1, err := tm.collateralEngine.GetPartyGeneralAccount("party1", tm.quoteAsset) 53 require.NoError(t, err) 54 require.Equal(t, "40000", gaBalance1.Balance.String()) 55 haBalance1, err := tm.collateralEngine.GetPartyHoldingAccount("party1", tm.quoteAsset) 56 require.NoError(t, err) 57 require.Equal(t, "60000", haBalance1.Balance.String()) 58 59 order2 := getGTCLimitOrder(tm, now, crypto.RandomHash(), types.SideSell, "party2", 1, 32000) 60 tm.market.SubmitOrder(ctx, order2.IntoSubmission(), order2.Party, crypto.RandomHash()) 61 62 gaBalance2, err := tm.collateralEngine.GetPartyGeneralAccount("party2", tm.baseAsset) 63 require.NoError(t, err) 64 require.Equal(t, "4", gaBalance2.Balance.String()) 65 66 haBalance2, err := tm.collateralEngine.GetPartyHoldingAccount("party2", tm.baseAsset) 67 require.NoError(t, err) 68 require.Equal(t, "1", haBalance2.Balance.String()) 69 70 md := tm.market.GetMarketData() 71 require.Equal(t, types.MarketTradingModeOpeningAuction, md.MarketTradingMode) 72 73 order3 := getGTCLimitOrder(tm, now, crypto.RandomHash(), types.SideSell, "party3", 1, 30000) 74 tm.market.SubmitOrder(ctx, order3.IntoSubmission(), order3.Party, crypto.RandomHash()) 75 76 haBalance3, err := tm.collateralEngine.GetPartyHoldingAccount("party3", tm.baseAsset) 77 require.NoError(t, err) 78 require.Equal(t, "1", haBalance3.Balance.String()) 79 80 gaBalance3, err := tm.collateralEngine.GetPartyGeneralAccount("party3", tm.baseAsset) 81 require.NoError(t, err) 82 require.Equal(t, "1", gaBalance3.Balance.String()) 83 84 md = tm.market.GetMarketData() 85 require.Equal(t, types.MarketTradingModeOpeningAuction, md.MarketTradingMode) 86 tm.market.OnTick(ctx, now.Add(2*time.Second)) 87 md = tm.market.GetMarketData() 88 require.Equal(t, types.MarketTradingModeContinuous, md.MarketTradingMode) 89 90 // trade has been done between party1 and party3, lets check balances 91 // party 1 had 60k ETH in holding and 40k ETH in their general account. The bought 1 BTC for 30k ETH at the end of the opening auction so now they have: 92 // 11 BTC in their BTC general account (10 they had + 1 they bought) 93 // 40k ETH in their ETH general account 94 // 30k ETH in their holding account 95 gaBalance1, err = tm.collateralEngine.GetPartyGeneralAccount("party1", tm.quoteAsset) 96 require.NoError(t, err) 97 require.Equal(t, "40000", gaBalance1.Balance.String()) 98 99 haBalance1, err = tm.collateralEngine.GetPartyHoldingAccount("party1", tm.quoteAsset) 100 require.NoError(t, err) 101 require.Equal(t, "30000", haBalance1.Balance.String()) 102 103 gaBaseBalance1, err := tm.collateralEngine.GetPartyGeneralAccount("party1", tm.baseAsset) 104 require.NoError(t, err) 105 require.Equal(t, "11", gaBaseBalance1.Balance.String()) 106 107 // party 3 has 2 BTC and 500 ETH 108 // they sold 1 BTC and received 30k ETH so now they have: 109 // 1 BTC 110 // 30500 ETH 111 // and nothing in the holding account 112 gaBalance3, err = tm.collateralEngine.GetPartyGeneralAccount("party3", tm.quoteAsset) 113 require.NoError(t, err) 114 require.Equal(t, "30500", gaBalance3.Balance.String()) 115 116 gaBaseBalance3, err := tm.collateralEngine.GetPartyGeneralAccount("party3", tm.baseAsset) 117 require.NoError(t, err) 118 require.Equal(t, "1", gaBaseBalance3.Balance.String()) 119 120 haBalance3, err = tm.collateralEngine.GetPartyHoldingAccount("party3", tm.baseAsset) 121 require.NoError(t, err) 122 require.Equal(t, "0", haBalance3.Balance.String()) 123 } 124 125 func TestAmend(t *testing.T) { 126 now := time.Now() 127 ctx := context.Background() 128 ctx = vegacontext.WithTraceID(ctx, crypto.RandomHash()) 129 tm := newTestMarket(t, defaultPriceMonitorSettings, &types.AuctionDuration{Duration: 1}, now) 130 131 addAccountWithAmount(tm, "party1", 100000, "ETH") 132 tm.market.StartOpeningAuction(ctx) 133 134 order1 := getGTCLimitOrder(tm, now, crypto.RandomHash(), types.SideBuy, "party1", 2, 30000) 135 conf, err := tm.market.SubmitOrder(ctx, order1.IntoSubmission(), order1.Party, crypto.RandomHash()) 136 require.NoError(t, err) 137 138 gaBalance1, err := tm.collateralEngine.GetPartyGeneralAccount("party1", tm.quoteAsset) 139 require.NoError(t, err) 140 require.Equal(t, "40000", gaBalance1.Balance.String()) 141 haBalance1, err := tm.collateralEngine.GetPartyHoldingAccount("party1", tm.quoteAsset) 142 require.NoError(t, err) 143 require.Equal(t, "60000", haBalance1.Balance.String()) 144 145 // increase price reduce size, now we expect the holding to have only 40k and general 60k 146 conf, err = tm.market.AmendOrder(ctx, &types.OrderAmendment{OrderID: conf.Order.ID, Price: num.NewUint(40000), SizeDelta: -1}, "party1", crypto.RandomHash()) 147 require.NoError(t, err) 148 149 gaBalance1, err = tm.collateralEngine.GetPartyGeneralAccount("party1", tm.quoteAsset) 150 require.NoError(t, err) 151 require.Equal(t, "60000", gaBalance1.Balance.String()) 152 haBalance1, err = tm.collateralEngine.GetPartyHoldingAccount("party1", tm.quoteAsset) 153 require.NoError(t, err) 154 require.Equal(t, "40000", haBalance1.Balance.String()) 155 156 // increase size, reduce price 157 _, err = tm.market.AmendOrder(ctx, &types.OrderAmendment{OrderID: conf.Order.ID, Price: num.NewUint(20000), SizeDelta: 2}, "party1", crypto.RandomHash()) 158 require.NoError(t, err) 159 160 // should be back to 60k in holding and 40k in general 161 gaBalance1, err = tm.collateralEngine.GetPartyGeneralAccount("party1", tm.quoteAsset) 162 require.NoError(t, err) 163 require.Equal(t, "40000", gaBalance1.Balance.String()) 164 haBalance1, err = tm.collateralEngine.GetPartyHoldingAccount("party1", tm.quoteAsset) 165 require.NoError(t, err) 166 require.Equal(t, "60000", haBalance1.Balance.String()) 167 } 168 169 func TestMarketWithAllowedSellers(t *testing.T) { 170 now := time.Now() 171 ctx := context.Background() 172 ctx = vegacontext.WithTraceID(ctx, crypto.RandomHash()) 173 tm := newTestMarketWithAllowedSellers(t, defaultPriceMonitorSettings, &types.AuctionDuration{Duration: 1}, now, []string{"party1", "party2"}) 174 175 addAccountWithAmount(tm, "party1", 100000, "ETH") 176 addAccountWithAmount(tm, "party2", 100000, "ETH") 177 addAccountWithAmount(tm, "party3", 100000, "ETH") 178 addAccountWithAmount(tm, "party1", 100000, "BTC") 179 addAccountWithAmount(tm, "party2", 100000, "BTC") 180 addAccountWithAmount(tm, "party3", 100000, "BTC") 181 tm.market.StartOpeningAuction(ctx) 182 183 t.Run("allowed seller can post sell orders", func(t *testing.T) { 184 order1 := getGTCLimitOrder(tm, now, crypto.RandomHash(), types.SideSell, "party1", 1, 300) 185 _, err := tm.market.SubmitOrder(ctx, order1.IntoSubmission(), order1.Party, crypto.RandomHash()) 186 require.NoError(t, err) 187 order2 := getGTCLimitOrder(tm, now, crypto.RandomHash(), types.SideSell, "party2", 1, 300) 188 _, err = tm.market.SubmitOrder(ctx, order2.IntoSubmission(), order2.Party, crypto.RandomHash()) 189 require.NoError(t, err) 190 }) 191 192 t.Run("allowed seller can post buy orders", func(t *testing.T) { 193 order1 := getGTCLimitOrder(tm, now, crypto.RandomHash(), types.SideBuy, "party1", 2, 200) 194 _, err := tm.market.SubmitOrder(ctx, order1.IntoSubmission(), order1.Party, crypto.RandomHash()) 195 require.NoError(t, err) 196 order2 := getGTCLimitOrder(tm, now, crypto.RandomHash(), types.SideBuy, "party2", 2, 200) 197 _, err = tm.market.SubmitOrder(ctx, order2.IntoSubmission(), order2.Party, crypto.RandomHash()) 198 require.NoError(t, err) 199 }) 200 201 t.Run("non allowed seller cannot post sell orders", func(t *testing.T) { 202 order1 := getGTCLimitOrder(tm, now, crypto.RandomHash(), types.SideSell, "party3", 2, 300) 203 _, err := tm.market.SubmitOrder(ctx, order1.IntoSubmission(), order1.Party, crypto.RandomHash()) 204 require.EqualError(t, err, "sell order not allowed") 205 }) 206 207 t.Run("non allowed seller can post buy orders", func(t *testing.T) { 208 order1 := getGTCLimitOrder(tm, now, crypto.RandomHash(), types.SideBuy, "party3", 2, 200) 209 _, err := tm.market.SubmitOrder(ctx, order1.IntoSubmission(), order1.Party, crypto.RandomHash()) 210 require.NoError(t, err) 211 }) 212 213 t.Run("exit auction", func(t *testing.T) { 214 order1 := getGTCLimitOrder(tm, now, crypto.RandomHash(), types.SideBuy, "party1", 2, 30000) 215 _, err := tm.market.SubmitOrder(ctx, order1.IntoSubmission(), order1.Party, crypto.RandomHash()) 216 require.NoError(t, err) 217 218 order2 := getGTCLimitOrder(tm, now, crypto.RandomHash(), types.SideSell, "party2", 1, 30000) 219 _, err = tm.market.SubmitOrder(ctx, order2.IntoSubmission(), order2.Party, crypto.RandomHash()) 220 require.NoError(t, err) 221 222 tm.market.OnTick(ctx, now.Add(2*time.Second)) 223 md := tm.market.GetMarketData() 224 require.Equal(t, types.MarketTradingModeContinuous, md.MarketTradingMode) 225 }) 226 227 t.Run("increase max stop orders per parties", func(t *testing.T) { 228 tm.market.OnMarketPartiesMaximumStopOrdersUpdate( 229 context.Background(), num.NewUint(1000)) 230 }) 231 232 t.Run("allowed seller can post sell stop orders", func(t *testing.T) { 233 idgen := idgeneration.New(crypto.RandomHash()) 234 order1 := getStopOrderSubmission(tm, now, crypto.RandomHash(), types.SideSell, types.SideBuy, "party1", 1, 300) 235 _, err := tm.market.SubmitStopOrdersWithIDGeneratorAndOrderIDs(ctx, order1, "party1", idgen, ptr.From(idgen.NextID()), ptr.From(idgen.NextID())) 236 require.NoError(t, err) 237 238 order2 := getStopOrderSubmission(tm, now, crypto.RandomHash(), types.SideSell, types.SideBuy, "party2", 1, 300) 239 _, err = tm.market.SubmitStopOrdersWithIDGeneratorAndOrderIDs(ctx, order2, "party2", idgen, ptr.From(idgen.NextID()), ptr.From(idgen.NextID())) 240 require.NoError(t, err) 241 242 order3 := getStopOrderSubmission(tm, now, crypto.RandomHash(), types.SideBuy, types.SideSell, "party1", 1, 300) 243 _, err = tm.market.SubmitStopOrdersWithIDGeneratorAndOrderIDs(ctx, order3, "party1", idgen, ptr.From(idgen.NextID()), ptr.From(idgen.NextID())) 244 require.NoError(t, err) 245 246 order4 := getStopOrderSubmission(tm, now, crypto.RandomHash(), types.SideBuy, types.SideSell, "party2", 1, 300) 247 _, err = tm.market.SubmitStopOrdersWithIDGeneratorAndOrderIDs(ctx, order4, "party2", idgen, ptr.From(idgen.NextID()), ptr.From(idgen.NextID())) 248 require.NoError(t, err) 249 }) 250 251 t.Run("non allowed seller cannot post sell stop orders", func(t *testing.T) { 252 idgen := idgeneration.New(crypto.RandomHash()) 253 order1 := getStopOrderSubmission(tm, now, crypto.RandomHash(), types.SideSell, types.SideBuy, "party3", 1, 300) 254 _, err := tm.market.SubmitStopOrdersWithIDGeneratorAndOrderIDs(ctx, order1, "party3", idgen, ptr.From(idgen.NextID()), ptr.From(idgen.NextID())) 255 require.EqualError(t, err, "sell order not allowed") 256 257 order2 := getStopOrderSubmission(tm, now, crypto.RandomHash(), types.SideBuy, types.SideSell, "party3", 1, 300) 258 _, err = tm.market.SubmitStopOrdersWithIDGeneratorAndOrderIDs(ctx, order2, "party3", idgen, ptr.From(idgen.NextID()), ptr.From(idgen.NextID())) 259 require.EqualError(t, err, "sell order not allowed") 260 }) 261 } 262 263 func TestCancelAll(t *testing.T) { 264 now := time.Now() 265 ctx := context.Background() 266 ctx = vegacontext.WithTraceID(ctx, crypto.RandomHash()) 267 tm := newTestMarket(t, defaultPriceMonitorSettings, &types.AuctionDuration{Duration: 1}, now) 268 269 addAccountWithAmount(tm, "party1", 100000, "ETH") 270 tm.market.StartOpeningAuction(ctx) 271 272 order1 := getGTCLimitOrder(tm, now, crypto.RandomHash(), types.SideBuy, "party1", 2, 30000) 273 _, err := tm.market.SubmitOrder(ctx, order1.IntoSubmission(), order1.Party, crypto.RandomHash()) 274 require.NoError(t, err) 275 276 gaBalance1, err := tm.collateralEngine.GetPartyGeneralAccount("party1", tm.quoteAsset) 277 require.NoError(t, err) 278 require.Equal(t, "40000", gaBalance1.Balance.String()) 279 haBalance1, err := tm.collateralEngine.GetPartyHoldingAccount("party1", tm.quoteAsset) 280 require.NoError(t, err) 281 require.Equal(t, "60000", haBalance1.Balance.String()) 282 283 order2 := getGTCLimitOrder(tm, now, crypto.RandomHash(), types.SideBuy, "party1", 2, 15000) 284 _, err = tm.market.SubmitOrder(ctx, order2.IntoSubmission(), order1.Party, crypto.RandomHash()) 285 require.NoError(t, err) 286 287 gaBalance1, err = tm.collateralEngine.GetPartyGeneralAccount("party1", tm.quoteAsset) 288 require.NoError(t, err) 289 require.Equal(t, "10000", gaBalance1.Balance.String()) 290 haBalance1, err = tm.collateralEngine.GetPartyHoldingAccount("party1", tm.quoteAsset) 291 require.NoError(t, err) 292 require.Equal(t, "90000", haBalance1.Balance.String()) 293 294 tm.market.CancelAllOrders(ctx, "party1") 295 gaBalance1, err = tm.collateralEngine.GetPartyGeneralAccount("party1", tm.quoteAsset) 296 require.NoError(t, err) 297 require.Equal(t, "100000", gaBalance1.Balance.String()) 298 haBalance1, err = tm.collateralEngine.GetPartyHoldingAccount("party1", tm.quoteAsset) 299 require.NoError(t, err) 300 require.Equal(t, "0", haBalance1.Balance.String()) 301 } 302 303 func TestCancelSingleOrder(t *testing.T) { 304 now := time.Now() 305 ctx := context.Background() 306 ctx = vegacontext.WithTraceID(ctx, crypto.RandomHash()) 307 tm := newTestMarket(t, defaultPriceMonitorSettings, &types.AuctionDuration{Duration: 1}, now) 308 309 addAccountWithAmount(tm, "party1", 100000, "ETH") 310 addAccountWithAmount(tm, "party2", 5, "BTC") 311 tm.market.StartOpeningAuction(ctx) 312 313 order1 := getGTCLimitOrder(tm, now, crypto.RandomHash(), types.SideBuy, "party1", 2, 30000) 314 conf1, err := tm.market.SubmitOrder(ctx, order1.IntoSubmission(), order1.Party, crypto.RandomHash()) 315 require.NoError(t, err) 316 317 gaBalance1, err := tm.collateralEngine.GetPartyGeneralAccount("party1", tm.quoteAsset) 318 require.NoError(t, err) 319 require.Equal(t, "40000", gaBalance1.Balance.String()) 320 haBalance1, err := tm.collateralEngine.GetPartyHoldingAccount("party1", tm.quoteAsset) 321 require.NoError(t, err) 322 require.Equal(t, "60000", haBalance1.Balance.String()) 323 324 order2 := getGTCLimitOrder(tm, now, crypto.RandomHash(), types.SideBuy, "party1", 2, 15000) 325 conf2, err := tm.market.SubmitOrder(ctx, order2.IntoSubmission(), order1.Party, crypto.RandomHash()) 326 require.NoError(t, err) 327 328 gaBalance1, err = tm.collateralEngine.GetPartyGeneralAccount("party1", tm.quoteAsset) 329 require.NoError(t, err) 330 require.Equal(t, "10000", gaBalance1.Balance.String()) 331 haBalance1, err = tm.collateralEngine.GetPartyHoldingAccount("party1", tm.quoteAsset) 332 require.NoError(t, err) 333 require.Equal(t, "90000", haBalance1.Balance.String()) 334 335 // cancel the second order, now we should have 336 tm.market.CancelOrder(ctx, "party1", conf2.Order.ID, crypto.RandomHash()) 337 gaBalance1, err = tm.collateralEngine.GetPartyGeneralAccount("party1", tm.quoteAsset) 338 require.NoError(t, err) 339 require.Equal(t, "40000", gaBalance1.Balance.String()) 340 haBalance1, err = tm.collateralEngine.GetPartyHoldingAccount("party1", tm.quoteAsset) 341 require.NoError(t, err) 342 require.Equal(t, "60000", haBalance1.Balance.String()) 343 344 // cancel the first order to have the full amount back in the general account 345 tm.market.CancelOrder(ctx, "party1", conf1.Order.ID, crypto.RandomHash()) 346 gaBalance1, err = tm.collateralEngine.GetPartyGeneralAccount("party1", tm.quoteAsset) 347 require.NoError(t, err) 348 require.Equal(t, "100000", gaBalance1.Balance.String()) 349 haBalance1, err = tm.collateralEngine.GetPartyHoldingAccount("party1", tm.quoteAsset) 350 require.NoError(t, err) 351 require.Equal(t, "0", haBalance1.Balance.String()) 352 } 353 354 func TestInsufficientCoverInSubmit(t *testing.T) { 355 now := time.Now() 356 ctx := context.Background() 357 ctx = vegacontext.WithTraceID(ctx, crypto.RandomHash()) 358 tm := newTestMarket(t, defaultPriceMonitorSettings, &types.AuctionDuration{Duration: 1}, now) 359 360 addAccountWithAmount(tm, "party1", 100000, "ETH") 361 tm.market.StartOpeningAuction(ctx) 362 363 // submit an order with insufficient cover should fail 364 order1 := getGTCLimitOrder(tm, now, crypto.RandomHash(), types.SideBuy, "party1", 4, 30000) 365 _, err := tm.market.SubmitOrder(ctx, order1.IntoSubmission(), order1.Party, crypto.RandomHash()) 366 require.Equal(t, "party does not have sufficient balance to cover the trade and fees", err.Error()) 367 368 gaBalance1, err := tm.collateralEngine.GetPartyGeneralAccount("party1", tm.quoteAsset) 369 require.NoError(t, err) 370 require.Equal(t, "100000", gaBalance1.Balance.String()) 371 372 // now submit a valid one 373 order1 = getGTCLimitOrder(tm, now, crypto.RandomHash(), types.SideBuy, "party1", 2, 30000) 374 _, err = tm.market.SubmitOrder(ctx, order1.IntoSubmission(), order1.Party, crypto.RandomHash()) 375 require.NoError(t, err) 376 377 gaBalance1, err = tm.collateralEngine.GetPartyGeneralAccount("party1", tm.quoteAsset) 378 require.NoError(t, err) 379 require.Equal(t, "40000", gaBalance1.Balance.String()) 380 haBalance1, err := tm.collateralEngine.GetPartyHoldingAccount("party1", tm.quoteAsset) 381 require.NoError(t, err) 382 require.Equal(t, "60000", haBalance1.Balance.String()) 383 384 // and again an invalid one 385 order2 := getGTCLimitOrder(tm, now, crypto.RandomHash(), types.SideBuy, "party1", 2, 30000) 386 _, err = tm.market.SubmitOrder(ctx, order2.IntoSubmission(), order2.Party, crypto.RandomHash()) 387 require.Equal(t, "party does not have sufficient balance to cover the trade and fees", err.Error()) 388 389 // and again a valid one 390 order2 = getGTCLimitOrder(tm, now, crypto.RandomHash(), types.SideBuy, "party1", 2, 20000) 391 _, err = tm.market.SubmitOrder(ctx, order2.IntoSubmission(), order2.Party, crypto.RandomHash()) 392 require.NoError(t, err) 393 394 gaBalance1, err = tm.collateralEngine.GetPartyGeneralAccount("party1", tm.quoteAsset) 395 require.NoError(t, err) 396 require.Equal(t, "0", gaBalance1.Balance.String()) 397 haBalance1, err = tm.collateralEngine.GetPartyHoldingAccount("party1", tm.quoteAsset) 398 require.NoError(t, err) 399 require.Equal(t, "100000", haBalance1.Balance.String()) 400 } 401 402 func TestNoValidAccount(t *testing.T) { 403 now := time.Now() 404 ctx := context.Background() 405 ctx = vegacontext.WithTraceID(ctx, crypto.RandomHash()) 406 tm := newTestMarket(t, defaultPriceMonitorSettings, &types.AuctionDuration{Duration: 1}, now) 407 408 tm.market.StartOpeningAuction(ctx) 409 410 // submit an order with insufficient cover should fail 411 order1 := getGTCLimitOrder(tm, now, crypto.RandomHash(), types.SideBuy, "party1", 4, 30000) 412 _, err := tm.market.SubmitOrder(ctx, order1.IntoSubmission(), order1.Party, crypto.RandomHash()) 413 require.Error(t, common.ErrPartyInsufficientAssetBalance, err) 414 } 415 416 func TestPartialFill(t *testing.T) { 417 now := time.Now() 418 ctx := context.Background() 419 ctx = vegacontext.WithTraceID(ctx, crypto.RandomHash()) 420 tm := newTestMarket(t, defaultPriceMonitorSettings, &types.AuctionDuration{Duration: 1}, now) 421 422 addAccountWithAmount(tm, "party1", 100000, "ETH") 423 addAccountWithAmount(tm, "party2", 5, "BTC") 424 425 tm.market.StartOpeningAuction(ctx) 426 427 order1 := getGTCLimitOrder(tm, now, crypto.RandomHash(), types.SideBuy, "party1", 2, 30000) 428 conf1, err := tm.market.SubmitOrder(ctx, order1.IntoSubmission(), order1.Party, crypto.RandomHash()) 429 require.NoError(t, err) 430 431 order2 := getGTCLimitOrder(tm, now, crypto.RandomHash(), types.SideSell, "party2", 1, 30000) 432 _, err = tm.market.SubmitOrder(ctx, order2.IntoSubmission(), order2.Party, crypto.RandomHash()) 433 require.NoError(t, err) 434 435 tm.market.OnTick(ctx, now.Add(2*time.Second)) 436 md := tm.market.GetMarketData() 437 require.Equal(t, types.MarketTradingModeContinuous, md.MarketTradingMode) 438 439 // expect one trade of size 1 440 gaBalance1, err := tm.collateralEngine.GetPartyGeneralAccount("party1", tm.quoteAsset) 441 require.NoError(t, err) 442 require.Equal(t, "40000", gaBalance1.Balance.String()) 443 haBalance1, err := tm.collateralEngine.GetPartyHoldingAccount("party1", tm.quoteAsset) 444 require.NoError(t, err) 445 require.Equal(t, "30000", haBalance1.Balance.String()) 446 gaBaseBalance1, err := tm.collateralEngine.GetPartyGeneralAccount("party1", tm.baseAsset) 447 require.NoError(t, err) 448 require.Equal(t, "1", gaBaseBalance1.Balance.String()) 449 450 // party 2 should get the 30k quote and should have 1 less base 451 gaBaseBalance2, err := tm.collateralEngine.GetPartyGeneralAccount("party2", tm.baseAsset) 452 require.NoError(t, err) 453 require.Equal(t, "4", gaBaseBalance2.Balance.String()) 454 haBaseBalance2, err := tm.collateralEngine.GetPartyHoldingAccount("party2", tm.baseAsset) 455 require.NoError(t, err) 456 require.Equal(t, "0", haBaseBalance2.Balance.String()) 457 gaBalance2, err := tm.collateralEngine.GetPartyGeneralAccount("party2", tm.quoteAsset) 458 require.NoError(t, err) 459 require.Equal(t, "30000", gaBalance2.Balance.String()) 460 461 tm.market.OnTick(ctx, now.Add(2*time.Second)) 462 md = tm.market.GetMarketData() 463 require.Equal(t, types.MarketTradingModeContinuous, md.MarketTradingMode) 464 465 // increase the remaining size to 2, decrease the size to 29900 466 conf, err := tm.market.AmendOrder(ctx, &types.OrderAmendment{OrderID: conf1.Order.ID, SizeDelta: 1, Price: num.NewUint(29900)}, "party1", crypto.RandomHash()) 467 require.NoError(t, err) 468 require.Equal(t, 0, len(conf.Trades)) 469 gaBalance1, err = tm.collateralEngine.GetPartyGeneralAccount("party1", tm.quoteAsset) 470 require.NoError(t, err) 471 require.Equal(t, "10200", gaBalance1.Balance.String()) 472 haBalance1, err = tm.collateralEngine.GetPartyHoldingAccount("party1", tm.quoteAsset) 473 require.NoError(t, err) 474 require.Equal(t, "59800", haBalance1.Balance.String()) 475 476 tm.market.OnTick(ctx, now.Add(2*time.Second)) 477 md = tm.market.GetMarketData() 478 require.Equal(t, types.MarketTradingModeContinuous, md.MarketTradingMode) 479 480 // now fill some and then cancel 481 order3 := getGTCLimitOrder(tm, now, crypto.RandomHash(), types.SideSell, "party2", 1, 29900) 482 conf3, err := tm.market.SubmitOrder(ctx, order3.IntoSubmission(), order3.Party, crypto.RandomHash()) 483 require.NoError(t, err) 484 require.Equal(t, 1, len(conf3.Trades)) 485 486 gaBalance1, err = tm.collateralEngine.GetPartyGeneralAccount("party1", tm.quoteAsset) 487 require.NoError(t, err) 488 require.Equal(t, "10320", gaBalance1.Balance.String()) // maker fees paid to party1 489 haBalance1, err = tm.collateralEngine.GetPartyHoldingAccount("party1", tm.quoteAsset) 490 require.NoError(t, err) 491 require.Equal(t, "29900", haBalance1.Balance.String()) 492 gaBaseBalance1, err = tm.collateralEngine.GetPartyGeneralAccount("party1", tm.baseAsset) 493 require.NoError(t, err) 494 require.Equal(t, "2", gaBaseBalance1.Balance.String()) 495 496 gaBaseBalance2, err = tm.collateralEngine.GetPartyGeneralAccount("party2", tm.baseAsset) 497 require.NoError(t, err) 498 require.Equal(t, "3", gaBaseBalance2.Balance.String()) 499 haBaseBalance2, err = tm.collateralEngine.GetPartyHoldingAccount("party2", tm.baseAsset) 500 require.NoError(t, err) 501 require.Equal(t, "0", haBaseBalance2.Balance.String()) 502 gaBalance2, err = tm.collateralEngine.GetPartyGeneralAccount("party2", tm.quoteAsset) 503 require.NoError(t, err) 504 // party2 is the aggressor so they pay the fees which are deducted from their 29990 payment leaving 29750 (240 paid in fees) 505 require.Equal(t, "59750", gaBalance2.Balance.String()) 506 507 // cancel all orders for party1 508 tm.market.CancelAllOrders(ctx, "party1") 509 gaBalance1, err = tm.collateralEngine.GetPartyGeneralAccount("party1", tm.quoteAsset) 510 require.NoError(t, err) 511 require.Equal(t, "40220", gaBalance1.Balance.String()) // maker fees paid to party1 512 haBalance1, err = tm.collateralEngine.GetPartyHoldingAccount("party1", tm.quoteAsset) 513 require.NoError(t, err) 514 require.Equal(t, "0", haBalance1.Balance.String()) 515 gaBaseBalance1, err = tm.collateralEngine.GetPartyGeneralAccount("party1", tm.baseAsset) 516 require.NoError(t, err) 517 require.Equal(t, "2", gaBaseBalance1.Balance.String()) 518 } 519 520 func TestIncreaseHolding(t *testing.T) { 521 now := time.Now() 522 ctx := context.Background() 523 ctx = vegacontext.WithTraceID(ctx, crypto.RandomHash()) 524 tm := newTestMarket(t, defaultPriceMonitorSettings, &types.AuctionDuration{Duration: 1}, now) 525 526 addAccountWithAmount(tm, "party1", 100000, "ETH") 527 addAccountWithAmount(tm, "party2", 5, "BTC") 528 529 tm.market.StartOpeningAuction(ctx) 530 531 order1 := getGTCLimitOrder(tm, now, crypto.RandomHash(), types.SideBuy, "party1", 2, 30000) 532 conf1, err := tm.market.SubmitOrder(ctx, order1.IntoSubmission(), order1.Party, crypto.RandomHash()) 533 require.NoError(t, err) 534 535 gaBalance1, err := tm.collateralEngine.GetPartyGeneralAccount("party1", tm.quoteAsset) 536 require.NoError(t, err) 537 require.Equal(t, "40000", gaBalance1.Balance.String()) 538 haBalance1, err := tm.collateralEngine.GetPartyHoldingAccount("party1", tm.quoteAsset) 539 require.NoError(t, err) 540 require.Equal(t, "60000", haBalance1.Balance.String()) 541 542 _, err = tm.market.AmendOrder(ctx, &types.OrderAmendment{OrderID: conf1.Order.ID, Price: num.NewUint(25000), SizeDelta: 1}, "party1", crypto.RandomHash()) 543 require.NoError(t, err) 544 545 gaBalance1, err = tm.collateralEngine.GetPartyGeneralAccount("party1", tm.quoteAsset) 546 require.NoError(t, err) 547 require.Equal(t, "25000", gaBalance1.Balance.String()) 548 haBalance1, err = tm.collateralEngine.GetPartyHoldingAccount("party1", tm.quoteAsset) 549 require.NoError(t, err) 550 require.Equal(t, "75000", haBalance1.Balance.String()) 551 } 552 553 func TestDecreaseHolding(t *testing.T) { 554 now := time.Now() 555 ctx := context.Background() 556 ctx = vegacontext.WithTraceID(ctx, crypto.RandomHash()) 557 tm := newTestMarket(t, defaultPriceMonitorSettings, &types.AuctionDuration{Duration: 1}, now) 558 559 addAccountWithAmount(tm, "party1", 100000, "ETH") 560 addAccountWithAmount(tm, "party2", 5, "BTC") 561 562 tm.market.StartOpeningAuction(ctx) 563 564 order1 := getGTCLimitOrder(tm, now, crypto.RandomHash(), types.SideBuy, "party1", 2, 30000) 565 conf1, err := tm.market.SubmitOrder(ctx, order1.IntoSubmission(), order1.Party, crypto.RandomHash()) 566 require.NoError(t, err) 567 568 gaBalance1, err := tm.collateralEngine.GetPartyGeneralAccount("party1", tm.quoteAsset) 569 require.NoError(t, err) 570 require.Equal(t, "40000", gaBalance1.Balance.String()) 571 haBalance1, err := tm.collateralEngine.GetPartyHoldingAccount("party1", tm.quoteAsset) 572 require.NoError(t, err) 573 require.Equal(t, "60000", haBalance1.Balance.String()) 574 575 _, err = tm.market.AmendOrder(ctx, &types.OrderAmendment{OrderID: conf1.Order.ID, Price: num.NewUint(45000), SizeDelta: -1}, "party1", crypto.RandomHash()) 576 require.NoError(t, err) 577 578 gaBalance1, err = tm.collateralEngine.GetPartyGeneralAccount("party1", tm.quoteAsset) 579 require.NoError(t, err) 580 require.Equal(t, "55000", gaBalance1.Balance.String()) 581 haBalance1, err = tm.collateralEngine.GetPartyHoldingAccount("party1", tm.quoteAsset) 582 require.NoError(t, err) 583 require.Equal(t, "45000", haBalance1.Balance.String()) 584 }