code.vegaprotocol.io/vega@v0.79.0/core/execution/future/bond_slashing_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 future_test 17 18 import ( 19 "context" 20 "fmt" 21 "testing" 22 "time" 23 24 "code.vegaprotocol.io/vega/core/types" 25 vegacontext "code.vegaprotocol.io/vega/libs/context" 26 vgcrypto "code.vegaprotocol.io/vega/libs/crypto" 27 "code.vegaprotocol.io/vega/libs/num" 28 29 "github.com/golang/mock/gomock" 30 "github.com/stretchr/testify/assert" 31 "github.com/stretchr/testify/require" 32 ) 33 34 //nolint:unparam 35 func setMarkPrice(t *testing.T, mkt *testMarket, duration *types.AuctionDuration, now time.Time, price uint64) { 36 t.Helper() 37 // all parties 38 parties := []string{"oo-p1", "oo-p4", "oo-p2", "oo-p3"} 39 // create accounts for the parties 40 for _, p := range parties { 41 addAccount(t, mkt, p) 42 } 43 delta := num.NewUint(10) 44 mPrice := num.NewUint(price) 45 orders := []*types.Order{ 46 { 47 MarketID: mkt.market.GetID(), 48 Party: parties[0], 49 Side: types.SideBuy, 50 Price: num.UintZero().Sub(mPrice, delta), 51 Size: 1, 52 Remaining: 1, 53 TimeInForce: types.OrderTimeInForceGTC, 54 Type: types.OrderTypeLimit, 55 CreatedAt: now.UnixNano(), 56 Reference: "oo-no-trade-buy", 57 }, 58 { 59 MarketID: mkt.market.GetID(), 60 Party: parties[2], 61 Side: types.SideBuy, 62 Price: mPrice, 63 Size: 1, 64 Remaining: 1, 65 TimeInForce: types.OrderTimeInForceGFA, 66 Type: types.OrderTypeLimit, 67 CreatedAt: now.UnixNano(), 68 Reference: "oo-trade-buy", 69 }, 70 { 71 MarketID: mkt.market.GetID(), 72 Party: parties[3], 73 Side: types.SideSell, 74 Price: mPrice, 75 Size: 1, 76 Remaining: 1, 77 TimeInForce: types.OrderTimeInForceGFA, 78 Type: types.OrderTypeLimit, 79 CreatedAt: now.UnixNano(), 80 Reference: "oo-trade-sell", 81 }, 82 { 83 MarketID: mkt.market.GetID(), 84 Party: parties[1], 85 Side: types.SideSell, 86 Price: num.Sum(mPrice, delta), 87 Size: 1, 88 Remaining: 1, 89 TimeInForce: types.OrderTimeInForceGTC, 90 Type: types.OrderTypeLimit, 91 CreatedAt: now.UnixNano(), 92 Reference: "oo-no-trade-sell", 93 }, 94 } 95 for _, o := range orders { 96 _, err := mkt.market.SubmitOrder(context.Background(), o) 97 require.NoError(t, err) 98 } 99 // now fast-forward the market so the auction ends 100 now = now.Add(time.Duration(duration.Duration+1) * time.Second) 101 ctx := vegacontext.WithTraceID(context.Background(), vgcrypto.RandomHash()) 102 mkt.now = now 103 mkt.market.OnTick(ctx, now) 104 105 // opening auction ended, mark-price set 106 mktData := mkt.market.GetMarketData() 107 require.NotNil(t, mktData) 108 require.Equal(t, types.MarketTradingModeContinuous, mktData.MarketTradingMode) 109 } 110 111 const lpprov = "lpprov-party" 112 113 func addSimpleLP(t *testing.T, mkt *testMarket, amt uint64) { 114 t.Helper() 115 116 lps := &types.LiquidityProvisionSubmission{ 117 Fee: num.DecimalFromFloat(0.01), 118 MarketID: mkt.market.GetID(), 119 CommitmentAmount: num.NewUint(amt), 120 } 121 require.NoError(t, mkt.market.SubmitLiquidityProvision( 122 context.Background(), lps, lpprov, vgcrypto.RandomHash(), 123 )) 124 } 125 126 func TestAcceptLiquidityProvisionWithSufficientFunds(t *testing.T) { 127 mainParty := "mainParty" 128 now := time.Unix(10, 0) 129 openingAuction := &types.AuctionDuration{ 130 Duration: 1, 131 } 132 tm := getTestMarket(t, now, nil, openingAuction) 133 initialMarkPrice := uint64(99) 134 ctx := context.Background() 135 136 asset := tm.asset 137 138 addAccountWithAmount(tm, lpprov, 50000000) 139 addSimpleLP(t, tm, 5000000) 140 // end opening auction 141 setMarkPrice(t, tm, openingAuction, now, initialMarkPrice) 142 143 mainPartyInitialDeposit := uint64(794) // 794 is the amount required to cover the initial margin on open orderss 144 addAccountWithAmount(tm, mainParty, mainPartyInitialDeposit) 145 146 tm.broker.EXPECT().Send(gomock.Any()).AnyTimes() 147 148 orderSell1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party1-sell-order-1", types.SideSell, mainParty, 5, initialMarkPrice+2) 149 150 confirmationSell, err := tm.market.SubmitOrder(ctx, orderSell1) 151 require.NotNil(t, confirmationSell) 152 require.NoError(t, err) 153 154 orderBuy1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party1-buy-order-1", types.SideBuy, mainParty, 4, initialMarkPrice-2) 155 156 confirmationBuy, err := tm.market.SubmitOrder(ctx, orderBuy1) 157 assert.NotNil(t, confirmationBuy) 158 assert.NoError(t, err) 159 160 require.Equal(t, 0, len(confirmationBuy.Trades)) 161 162 lp1 := &types.LiquidityProvisionSubmission{ 163 MarketID: tm.market.GetID(), 164 CommitmentAmount: num.NewUint(200), 165 Fee: num.DecimalFromFloat(0.05), 166 } 167 168 err = tm.market.SubmitLiquidityProvision(ctx, lp1, mainParty, vgcrypto.RandomHash()) 169 require.NoError(t, err) 170 171 bondAcc, err := tm.collateralEngine.GetOrCreatePartyBondAccount(ctx, mainParty, tm.mktCfg.ID, asset) 172 require.NoError(t, err) 173 require.NotNil(t, bondAcc) 174 require.Equal(t, lp1.CommitmentAmount, bondAcc.Balance) 175 } 176 177 func TestRejectLiquidityProvisionWithInsufficientFundsForInitialMargin(t *testing.T) { 178 mainParty := "mainParty" 179 now := time.Unix(10, 0) 180 openingAuction := &types.AuctionDuration{ 181 Duration: 1, 182 } 183 tm := getTestMarket(t, now, nil, openingAuction) 184 initialMarkPrice := uint64(99) 185 ctx := context.Background() 186 187 asset := tm.asset 188 189 addAccountWithAmount(tm, lpprov, 5000000) 190 addSimpleLP(t, tm, 5000000) 191 // end opening auction 192 setMarkPrice(t, tm, openingAuction, now, initialMarkPrice) 193 194 mainPartyInitialDeposit := uint64(347) // 348 is the minimum required amount to meet the commitment amount and maintenance margin on resulting orders 195 transferResp := addAccountWithAmount(tm, mainParty, mainPartyInitialDeposit) 196 mainPartyGenAccID := transferResp.Entries[0].ToAccount 197 198 tm.broker.EXPECT().Send(gomock.Any()).AnyTimes() 199 200 orderSell1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party1-sell-order-1", types.SideSell, mainParty, 5, initialMarkPrice+2) 201 202 confirmationSell, err := tm.market.SubmitOrder(ctx, orderSell1) 203 require.NotNil(t, confirmationSell) 204 require.NoError(t, err) 205 206 orderBuy1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party1-buy-order-1", types.SideBuy, mainParty, 4, initialMarkPrice-2) 207 208 confirmationBuy, err := tm.market.SubmitOrder(ctx, orderBuy1) 209 assert.NotNil(t, confirmationBuy) 210 assert.NoError(t, err) 211 212 require.Equal(t, 0, len(confirmationBuy.Trades)) 213 214 lp1 := &types.LiquidityProvisionSubmission{ 215 MarketID: tm.market.GetID(), 216 CommitmentAmount: num.NewUint(200), 217 Fee: num.DecimalFromFloat(0.05), 218 } 219 220 // Assure that at least the commitment amount can be covered with the initial deposit, otherwise it's a trivial failure (LP can't even afford the bond) 221 require.Greater(t, mainPartyInitialDeposit, lp1.CommitmentAmount.Uint64()) 222 223 numLpsPriorToSubmission := tm.market.GetLPSCount() 224 225 err = tm.market.SubmitLiquidityProvision(ctx, lp1, mainParty, vgcrypto.RandomHash()) 226 require.Error(t, err) 227 228 assert.Equal(t, numLpsPriorToSubmission, tm.market.GetLPSCount()) 229 230 bondAcc, err := tm.collateralEngine.GetOrCreatePartyBondAccount(ctx, mainParty, tm.mktCfg.ID, asset) 231 require.NoError(t, err) 232 require.NotNil(t, bondAcc) 233 require.Equal(t, num.UintZero(), bondAcc.Balance) 234 235 insurancePoolAccID := fmt.Sprintf("%s*%s1", tm.market.GetID(), asset) 236 insurancePool, err := tm.collateralEngine.GetAccountByID(insurancePoolAccID) 237 require.NoError(t, err) 238 require.NotNil(t, insurancePool) 239 require.Equal(t, num.UintZero(), insurancePool.Balance) 240 241 // TODO: JEREMY: funds are staying in margin ACCOUNT, let's 242 // fix that latert. 243 marginAcc, err := tm.collateralEngine.GetPartyMarginAccount(tm.mktCfg.ID, mainParty, asset) 244 require.NoError(t, err) 245 require.NotNil(t, marginAcc) 246 247 exp := num.UintZero().Sub(num.NewUint(mainPartyInitialDeposit), marginAcc.Balance) 248 genAcc, err := tm.collateralEngine.GetAccountByID(mainPartyGenAccID.ID()) 249 require.NoError(t, err) 250 require.NotNil(t, genAcc) 251 require.Equal(t, genAcc.Balance, exp) 252 } 253 254 func TestCloseoutLPWhenCannotCoverMargin(t *testing.T) { 255 t.Skip("to be fixed @witold") 256 mainParty := "mainParty" 257 auxParty1 := "auxParty1" 258 now := time.Unix(10, 0) 259 ctx := context.Background() 260 openingAuction := &types.AuctionDuration{ 261 Duration: 1, 262 } 263 tm := getTestMarket(t, now, nil, openingAuction) 264 initialMarkPrice := uint64(99) 265 266 setMarkPrice(t, tm, openingAuction, now, initialMarkPrice) 267 268 asset := tm.asset 269 270 var mainPartyInitialDeposit uint64 = 527 // 794 is the minimum amount to cover additional orders after orderBuyAux1 fills 271 transferResp := addAccountWithAmount(tm, mainParty, mainPartyInitialDeposit) 272 mainPartyGenAccID := transferResp.Entries[0].ToAccount.ID() 273 addAccount(t, tm, auxParty1) 274 tm.broker.EXPECT().Send(gomock.Any()).AnyTimes() 275 276 orderSell1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party1-sell-order-1", types.SideSell, mainParty, 10, initialMarkPrice+2) 277 confirmationSell1, err := tm.market.SubmitOrder(ctx, orderSell1) 278 require.NotNil(t, confirmationSell1) 279 require.NoError(t, err) 280 281 orderSell2 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party1-sell-order-2", types.SideSell, mainParty, 1, initialMarkPrice+5) 282 confirmationSell2, err := tm.market.SubmitOrder(ctx, orderSell2) 283 require.NotNil(t, confirmationSell2) 284 require.NoError(t, err) 285 286 orderBuy1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party1-buy-order-1", types.SideBuy, mainParty, 4, initialMarkPrice-2) 287 288 confirmationBuy, err := tm.market.SubmitOrder(ctx, orderBuy1) 289 assert.NotNil(t, confirmationBuy) 290 assert.NoError(t, err) 291 292 require.Equal(t, 0, len(confirmationBuy.Trades)) 293 294 lp := &types.LiquidityProvisionSubmission{ 295 MarketID: tm.market.GetID(), 296 CommitmentAmount: num.NewUint(200), 297 Fee: num.DecimalFromFloat(0.05), 298 } 299 300 err = tm.market.SubmitLiquidityProvision(ctx, lp, mainParty, vgcrypto.RandomHash()) 301 require.NoError(t, err) 302 303 require.Equal(t, 1, tm.market.GetLPSCount()) 304 305 bondAcc, err := tm.collateralEngine.GetOrCreatePartyBondAccount(ctx, mainParty, tm.mktCfg.ID, asset) 306 require.NoError(t, err) 307 require.NotNil(t, bondAcc) 308 require.Equal(t, lp.CommitmentAmount, bondAcc.Balance) 309 310 genAcc, err := tm.collateralEngine.GetAccountByID(mainPartyGenAccID) 311 require.NoError(t, err) 312 require.NotNil(t, genAcc) 313 require.Equal(t, genAcc.Balance, num.UintZero()) 314 315 insurancePoolAccID := fmt.Sprintf("%s*%s1", tm.market.GetID(), asset) 316 insurancePool, err := tm.collateralEngine.GetAccountByID(insurancePoolAccID) 317 require.NoError(t, err) 318 insurancePoolBalanceBeforeLPCloseout := insurancePool.Balance.Clone() 319 require.Equal(t, num.UintZero(), insurancePoolBalanceBeforeLPCloseout) 320 321 orderBuyAux1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party2-buy-order-1", types.SideBuy, auxParty1, orderSell1.Size+1, orderSell1.Price.Uint64()) 322 confirmationBuyAux1, err := tm.market.SubmitOrder(ctx, orderBuyAux1) 323 require.NotNil(t, confirmationBuyAux1) 324 require.NoError(t, err) 325 require.Equal(t, 2, len(confirmationBuyAux1.Trades)) 326 327 assert.Equal(t, 0, tm.market.GetLPSCount()) 328 329 genAcc, err = tm.collateralEngine.GetAccountByID(mainPartyGenAccID) 330 require.NoError(t, err) 331 require.NotNil(t, genAcc) 332 require.Equal(t, num.UintZero(), genAcc.Balance) 333 334 bondAcc, err = tm.collateralEngine.GetOrCreatePartyBondAccount(ctx, mainParty, tm.mktCfg.ID, asset) 335 require.NoError(t, err) 336 require.NotNil(t, bondAcc) 337 require.Equal(t, num.UintZero(), bondAcc.Balance) 338 339 insurancePool, err = tm.collateralEngine.GetAccountByID(insurancePoolAccID) 340 require.NoError(t, err) 341 require.NotNil(t, insurancePool) 342 insurancePoolBalanceAfterLPCloseout := insurancePool.Balance.Clone() 343 require.Greater(t, insurancePoolBalanceAfterLPCloseout, insurancePoolBalanceBeforeLPCloseout) 344 } 345 346 func TestBondAccountNotUsedForMarginShortageWhenEnoughMoneyInGeneral(t *testing.T) { 347 t.Skip("to be fixed @witold") 348 mainParty := "mainParty" 349 auxParty1 := "auxParty1" 350 now := time.Unix(10, 0) 351 initialMarkPrice := uint64(99) 352 ctx := context.Background() 353 openingAuction := &types.AuctionDuration{ 354 Duration: 1, 355 } 356 tm := getTestMarket(t, now, nil, openingAuction) 357 358 setMarkPrice(t, tm, openingAuction, now, initialMarkPrice) 359 360 asset := tm.asset 361 362 var mainPartyInitialDeposit uint64 = 1020 // 1020 is the minimum required amount to cover margin without dipping into the bond account 363 transferResp := addAccountWithAmount(tm, mainParty, mainPartyInitialDeposit) 364 mainPartyGenAccID := transferResp.Entries[0].ToAccount.ID() 365 addAccount(t, tm, auxParty1) 366 tm.broker.EXPECT().Send(gomock.Any()).AnyTimes() 367 368 orderSell1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party1-sell-order-1", types.SideSell, mainParty, 5, initialMarkPrice+2) 369 confirmationSell1, err := tm.market.SubmitOrder(ctx, orderSell1) 370 require.NotNil(t, confirmationSell1) 371 require.NoError(t, err) 372 373 orderSell2 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party1-sell-order-2", types.SideSell, mainParty, 1, initialMarkPrice+5) 374 confirmationSell2, err := tm.market.SubmitOrder(ctx, orderSell2) 375 require.NotNil(t, confirmationSell2) 376 require.NoError(t, err) 377 378 orderBuy1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party1-buy-order-1", types.SideBuy, mainParty, 4, initialMarkPrice-2) 379 confirmationBuy, err := tm.market.SubmitOrder(ctx, orderBuy1) 380 assert.NotNil(t, confirmationBuy) 381 assert.NoError(t, err) 382 require.Equal(t, 0, len(confirmationBuy.Trades)) 383 384 lp := &types.LiquidityProvisionSubmission{ 385 MarketID: tm.market.GetID(), 386 CommitmentAmount: num.NewUint(200), 387 Fee: num.DecimalFromFloat(0.05), 388 } 389 390 err = tm.market.SubmitLiquidityProvision(ctx, lp, mainParty, vgcrypto.RandomHash()) 391 require.NoError(t, err) 392 393 bondAcc, err := tm.collateralEngine.GetOrCreatePartyBondAccount(ctx, mainParty, tm.mktCfg.ID, asset) 394 require.NoError(t, err) 395 require.NotNil(t, bondAcc) 396 require.Equal(t, lp.CommitmentAmount, bondAcc.Balance) 397 398 insurancePoolAccID := fmt.Sprintf("%s*%s1", tm.market.GetID(), asset) 399 insurancePool, err := tm.collateralEngine.GetAccountByID(insurancePoolAccID) 400 require.NoError(t, err) 401 insurancePoolBalanceBeforeMarketMove := insurancePool.Balance.Clone() 402 require.Equal(t, num.UintZero(), insurancePoolBalanceBeforeMarketMove) 403 404 orderBuyAux1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party2-buy-order-1", types.SideBuy, auxParty1, orderSell1.Size+1, orderSell1.Price.Uint64()) 405 confirmationBuyAux1, err := tm.market.SubmitOrder(ctx, orderBuyAux1) 406 require.NotNil(t, confirmationBuyAux1) 407 require.NoError(t, err) 408 require.Equal(t, 2, len(confirmationBuyAux1.Trades)) 409 410 genAcc, err := tm.collateralEngine.GetAccountByID(mainPartyGenAccID) 411 require.NoError(t, err) 412 require.NotNil(t, genAcc) 413 require.Equal(t, num.UintZero(), genAcc.Balance) 414 415 bondAcc, err = tm.collateralEngine.GetOrCreatePartyBondAccount(ctx, mainParty, tm.mktCfg.ID, asset) 416 require.NoError(t, err) 417 require.NotNil(t, bondAcc) 418 require.Equal(t, lp.CommitmentAmount, bondAcc.Balance) 419 420 insurancePool, err = tm.collateralEngine.GetAccountByID(insurancePoolAccID) 421 insurancePoolBalanceAfterMarketMove := insurancePool.Balance.Clone() 422 423 require.NoError(t, err) 424 require.NotNil(t, insurancePool) 425 require.Equal(t, num.UintZero(), insurancePoolBalanceAfterMarketMove) 426 } 427 428 func TestBondAccountUsedForMarginShortage_PenaltyPaidFromBondAccount(t *testing.T) { 429 t.Skip("to be fixed @witold") 430 mainParty := "mainParty" 431 auxParty1 := "auxParty1" 432 now := time.Unix(10, 0) 433 initialMarkPrice := uint64(99) 434 ctx := context.Background() 435 openingAuction := &types.AuctionDuration{ 436 Duration: 1, 437 } 438 tm := getTestMarket(t, now, nil, openingAuction) 439 440 setMarkPrice(t, tm, openingAuction, now, initialMarkPrice) 441 442 asset := tm.asset 443 444 bondPenaltyParameter := 0.1 445 tm.market.OnMarketLiquidityV2BondPenaltyFactorUpdate(num.DecimalFromFloat(bondPenaltyParameter)) 446 // No fees 447 tm.market.OnFeeFactorsInfrastructureFeeUpdate(ctx, num.DecimalFromFloat(0)) 448 tm.market.OnFeeFactorsMakerFeeUpdate(ctx, num.DecimalFromFloat(0)) 449 450 var mainPartyInitialDeposit uint64 = 1000 // 1020 is the minimum required amount to cover margin without dipping into the bond account 451 transferResp := addAccountWithAmount(tm, mainParty, mainPartyInitialDeposit) 452 mainPartyGenAccID := transferResp.Entries[0].ToAccount.ID() 453 mainPartyMarginAccID := fmt.Sprintf("%smainParty%s3", tm.market.GetID(), tm.asset) 454 addAccount(t, tm, auxParty1) 455 tm.broker.EXPECT().Send(gomock.Any()).AnyTimes() 456 457 orderSell1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party1-sell-order-1", types.SideSell, mainParty, 5, initialMarkPrice+2) 458 confirmationSell1, err := tm.market.SubmitOrder(ctx, orderSell1) 459 require.NotNil(t, confirmationSell1) 460 require.NoError(t, err) 461 462 orderSell2 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party1-sell-order-2", types.SideSell, mainParty, 1, initialMarkPrice+5) 463 confirmationSell2, err := tm.market.SubmitOrder(ctx, orderSell2) 464 require.NotNil(t, confirmationSell2) 465 require.NoError(t, err) 466 467 orderBuy1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party1-buy-order-1", types.SideBuy, mainParty, 4, initialMarkPrice-2) 468 469 confirmationBuy, err := tm.market.SubmitOrder(ctx, orderBuy1) 470 assert.NotNil(t, confirmationBuy) 471 assert.NoError(t, err) 472 473 require.Equal(t, 0, len(confirmationBuy.Trades)) 474 475 lp := &types.LiquidityProvisionSubmission{ 476 MarketID: tm.market.GetID(), 477 CommitmentAmount: num.NewUint(200), 478 Fee: num.DecimalFromFloat(0.0), 479 } 480 481 err = tm.market.SubmitLiquidityProvision(ctx, lp, mainParty, vgcrypto.RandomHash()) 482 require.NoError(t, err) 483 484 genAcc, err := tm.collateralEngine.GetAccountByID(mainPartyGenAccID) 485 require.NoError(t, err) 486 require.NotNil(t, genAcc) 487 require.False(t, genAcc.Balance.IsZero()) 488 genAccBalanceBeforeMarketMove := genAcc.Balance.Clone() 489 490 marginAcc, err := tm.collateralEngine.GetAccountByID(mainPartyMarginAccID) 491 require.NoError(t, err) 492 require.NotNil(t, marginAcc) 493 marginAccBalanceBeforeMarketMove := marginAcc.Balance.Clone() 494 495 bondAcc, err := tm.collateralEngine.GetOrCreatePartyBondAccount(ctx, mainParty, tm.mktCfg.ID, asset) 496 require.NoError(t, err) 497 require.NotNil(t, bondAcc) 498 bondAccBalanceBeforeMarketMove := bondAcc.Balance.Clone() 499 require.Equal(t, lp.CommitmentAmount, bondAccBalanceBeforeMarketMove) 500 501 insurancePoolAccID := fmt.Sprintf("%s*%s1", tm.market.GetID(), asset) 502 insurancePool, err := tm.collateralEngine.GetAccountByID(insurancePoolAccID) 503 require.NoError(t, err) 504 insurancePoolBalanceBeforeMarketMove := insurancePool.Balance.Clone() 505 require.Equal(t, num.UintZero(), insurancePoolBalanceBeforeMarketMove) 506 507 orderBuyAux1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party2-buy-order-1", types.SideBuy, auxParty1, orderSell1.Size+1, orderSell1.Price.Uint64()) 508 confirmationBuyAux1, err := tm.market.SubmitOrder(ctx, orderBuyAux1) 509 require.NotNil(t, confirmationBuyAux1) 510 require.NoError(t, err) 511 require.Equal(t, 2, len(confirmationBuyAux1.Trades)) 512 513 genAcc, err = tm.collateralEngine.GetAccountByID(mainPartyGenAccID) 514 require.NoError(t, err) 515 require.NotNil(t, genAcc) 516 genAccBalanceAfterMarketMove := genAcc.Balance.Clone() 517 require.True(t, genAcc.Balance.IsZero()) 518 519 marginAcc, err = tm.collateralEngine.GetAccountByID(mainPartyMarginAccID) 520 require.NoError(t, err) 521 require.NotNil(t, marginAcc) 522 marginAccBalanceAfterMarketMove := marginAcc.Balance.Clone() 523 524 bondAcc, err = tm.collateralEngine.GetOrCreatePartyBondAccount(ctx, mainParty, tm.mktCfg.ID, asset) 525 require.NoError(t, err) 526 require.NotNil(t, bondAcc) 527 bondAccBalanceAfterMarketMove := bondAcc.Balance.Clone() 528 require.Less(t, bondAccBalanceAfterMarketMove, bondAccBalanceBeforeMarketMove) 529 require.False(t, bondAccBalanceAfterMarketMove.IsZero()) 530 531 insurancePool, err = tm.collateralEngine.GetAccountByID(insurancePoolAccID) 532 insurancePoolBalanceAfterMarketMove := insurancePool.Balance.Clone() 533 534 require.NoError(t, err) 535 require.NotNil(t, insurancePool) 536 require.True(t, insurancePoolBalanceAfterMarketMove.GT(insurancePoolBalanceBeforeMarketMove)) 537 538 genAccBalanceChange, gNeg := num.UintZero().Delta(genAccBalanceAfterMarketMove, genAccBalanceBeforeMarketMove) 539 marginAccBalanceChange, mNeg := num.UintZero().Delta(marginAccBalanceAfterMarketMove, marginAccBalanceBeforeMarketMove) 540 insurancePoolBalanceChange, iNeg := num.UintZero().Delta(insurancePoolBalanceAfterMarketMove, insurancePoolBalanceBeforeMarketMove) 541 // assume all positive 542 expBB := num.Sum(bondAccBalanceBeforeMarketMove, genAccBalanceChange, marginAccBalanceChange, insurancePoolBalanceChange) 543 if gNeg { 544 // we've added, so subtract twice 545 expBB.Sub(expBB, num.Sum(genAccBalanceChange, genAccBalanceChange)) 546 } 547 if mNeg { 548 expBB.Sub(expBB, num.Sum(marginAccBalanceChange, marginAccBalanceChange)) 549 } 550 if iNeg { 551 expBB.Sub(expBB, num.Sum(insurancePoolBalanceChange, insurancePoolBalanceChange)) 552 } 553 554 require.Equal(t, expBB, bondAccBalanceAfterMarketMove) 555 } 556 557 func TestBondAccountUsedForMarginShortagePenaltyPaidFromMarginAccount_NoCloseout(t *testing.T) { 558 t.Skip("to be fixed @witold") 559 mainParty := "mainParty" 560 auxParty1 := "auxParty1" 561 now := time.Unix(10, 0) 562 initialMarkPrice := uint64(99) 563 ctx := context.Background() 564 openingAuction := &types.AuctionDuration{ 565 Duration: 1, 566 } 567 tm := getTestMarket(t, now, nil, openingAuction) 568 569 setMarkPrice(t, tm, openingAuction, now, initialMarkPrice) 570 571 asset := tm.asset 572 573 bondPenaltyParameter := 0.1 574 tm.market.OnMarketLiquidityV2BondPenaltyFactorUpdate(num.DecimalFromFloat(bondPenaltyParameter)) 575 576 var mainPartyInitialDeposit uint64 = 800 577 transferResp := addAccountWithAmount(tm, mainParty, mainPartyInitialDeposit) 578 mainPartyGenAccID := transferResp.Entries[0].ToAccount.ID() 579 mainPartyMarginAccID := fmt.Sprintf("%smainParty%s3", tm.market.GetID(), tm.asset) 580 addAccount(t, tm, auxParty1) 581 tm.broker.EXPECT().Send(gomock.Any()).AnyTimes() 582 583 orderSell1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party1-sell-order-1", types.SideSell, mainParty, 5, initialMarkPrice+2) 584 confirmationSell1, err := tm.market.SubmitOrder(ctx, orderSell1) 585 require.NotNil(t, confirmationSell1) 586 require.NoError(t, err) 587 588 orderSell2 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party1-sell-order-2", types.SideSell, mainParty, 1, initialMarkPrice+5) 589 confirmationSell2, err := tm.market.SubmitOrder(ctx, orderSell2) 590 require.NotNil(t, confirmationSell2) 591 require.NoError(t, err) 592 593 orderBuy1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party1-buy-order-1", types.SideBuy, mainParty, 4, initialMarkPrice-2) 594 595 confirmationBuy, err := tm.market.SubmitOrder(ctx, orderBuy1) 596 assert.NotNil(t, confirmationBuy) 597 assert.NoError(t, err) 598 599 require.Equal(t, 0, len(confirmationBuy.Trades)) 600 601 lp := &types.LiquidityProvisionSubmission{ 602 MarketID: tm.market.GetID(), 603 CommitmentAmount: num.NewUint(200), 604 Fee: num.DecimalFromFloat(0.05), 605 } 606 607 err = tm.market.SubmitLiquidityProvision(ctx, lp, mainParty, vgcrypto.RandomHash()) 608 require.NoError(t, err) 609 610 marginAcc, err := tm.collateralEngine.GetAccountByID(mainPartyMarginAccID) 611 require.NoError(t, err) 612 require.NotNil(t, marginAcc) 613 require.False(t, marginAcc.Balance.IsZero()) 614 615 bondAcc, err := tm.collateralEngine.GetOrCreatePartyBondAccount(ctx, mainParty, tm.mktCfg.ID, asset) 616 require.NoError(t, err) 617 require.NotNil(t, bondAcc) 618 bondAccBalanceBeforeMarketMove := bondAcc.Balance.Clone() 619 require.Equal(t, lp.CommitmentAmount, bondAccBalanceBeforeMarketMove) 620 621 insurancePoolAccID := fmt.Sprintf("%s*%s1", tm.market.GetID(), asset) 622 insurancePool, err := tm.collateralEngine.GetAccountByID(insurancePoolAccID) 623 require.NoError(t, err) 624 insurancePoolBalanceBeforeMarketMove := insurancePool.Balance.Clone() 625 626 // Add sell order so LP can be closed out 627 orderSellAux1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party2-buy-order-1", types.SideSell, auxParty1, 10, orderSell1.Price.Uint64()+1) 628 confirmationSellAux1, err := tm.market.SubmitOrder(ctx, orderSellAux1) 629 require.NotNil(t, confirmationSellAux1) 630 require.NoError(t, err) 631 require.Equal(t, 0, len(confirmationSellAux1.Trades)) 632 633 orderBuyAux1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party2-buy-order-1", types.SideBuy, auxParty1, orderSell1.Size+1, orderSell1.Price.Uint64()) 634 confirmationBuyAux1, err := tm.market.SubmitOrder(ctx, orderBuyAux1) 635 require.NotNil(t, confirmationBuyAux1) 636 require.NoError(t, err) 637 require.Equal(t, 2, len(confirmationBuyAux1.Trades)) 638 639 genAcc, err := tm.collateralEngine.GetAccountByID(mainPartyGenAccID) 640 require.NoError(t, err) 641 require.NotNil(t, genAcc) 642 require.True(t, genAcc.Balance.IsZero()) 643 644 marginAccount, err := tm.collateralEngine.GetAccountByID(mainPartyMarginAccID) 645 require.NoError(t, err) 646 require.False(t, marginAccount.Balance.IsZero()) 647 648 bondAcc, err = tm.collateralEngine.GetOrCreatePartyBondAccount(ctx, mainParty, tm.mktCfg.ID, asset) 649 require.NoError(t, err) 650 require.NotNil(t, bondAcc) 651 require.True(t, bondAcc.Balance.LT(bondAccBalanceBeforeMarketMove)) 652 require.True(t, bondAcc.Balance.IsZero()) 653 654 require.Equal(t, 1, tm.market.GetLPSCount()) 655 656 insurancePool, err = tm.collateralEngine.GetAccountByID(insurancePoolAccID) 657 658 require.NoError(t, err) 659 require.NotNil(t, insurancePool) 660 require.True(t, insurancePool.Balance.GT(insurancePoolBalanceBeforeMarketMove)) 661 require.True(t, bondAcc.Balance.IsZero()) 662 } 663 664 func TestBondAccountUsedForMarginShortagePenaltyNotPaidOnTransitionFromAuction(t *testing.T) { 665 t.Skip("to be fixed @witold") 666 mainParty := "mainParty" 667 auxParty1 := "auxParty1" 668 now := time.Unix(10, 0) 669 ctx := context.Background() 670 openingAuctionDuration := &types.AuctionDuration{Duration: 10} 671 tm := getTestMarket2(t, now, nil, openingAuctionDuration, true, 0.99) 672 673 mktData := tm.market.GetMarketData() 674 require.NotNil(t, mktData) 675 require.Equal(t, types.MarketTradingModeOpeningAuction, mktData.MarketTradingMode) 676 677 initialMarkPrice := uint64(99) 678 679 asset, err := tm.mktCfg.GetAssets() 680 require.NoError(t, err) 681 682 var mainPartyInitialDeposit uint64 = 784 // 794 is the minimum required amount to cover margin without dipping into the bond account 683 transferResp := addAccountWithAmount(tm, mainParty, mainPartyInitialDeposit) 684 mainPartyGenAccID := transferResp.Entries[0].ToAccount.ID() 685 addAccount(t, tm, auxParty1) 686 tm.broker.EXPECT().Send(gomock.Any()).AnyTimes() 687 688 orderSell1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party1-sell-order-1", types.SideSell, mainParty, 5, initialMarkPrice+2) 689 confirmationSell1, err := tm.market.SubmitOrder(ctx, orderSell1) 690 require.NotNil(t, confirmationSell1) 691 require.NoError(t, err) 692 693 orderBuy1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party1-buy-order-1", types.SideBuy, mainParty, 4, initialMarkPrice-2) 694 confirmationBuy1, err := tm.market.SubmitOrder(ctx, orderBuy1) 695 assert.NotNil(t, confirmationBuy1) 696 assert.NoError(t, err) 697 require.Equal(t, 0, len(confirmationBuy1.Trades)) 698 699 lp := &types.LiquidityProvisionSubmission{ 700 MarketID: tm.market.GetID(), 701 CommitmentAmount: num.NewUint(200), 702 Fee: num.DecimalFromFloat(0.05), 703 } 704 705 genAcc, err := tm.collateralEngine.GetAccountByID(mainPartyGenAccID) 706 require.NoError(t, err) 707 require.NotNil(t, genAcc) 708 genAccBalanceBeforeLPSubmission := genAcc.Balance.Clone() 709 require.False(t, genAcc.Balance.IsZero()) 710 711 err = tm.market.SubmitLiquidityProvision(ctx, lp, mainParty, vgcrypto.RandomHash()) 712 require.NoError(t, err) 713 714 genAcc, err = tm.collateralEngine.GetAccountByID(mainPartyGenAccID) 715 require.NoError(t, err) 716 require.NotNil(t, genAcc) 717 require.False(t, genAcc.Balance.IsZero()) 718 require.Equal(t, genAcc.Balance, num.UintZero().Sub(genAccBalanceBeforeLPSubmission, lp.CommitmentAmount)) 719 720 bondAcc, err := tm.collateralEngine.GetOrCreatePartyBondAccount(ctx, mainParty, tm.mktCfg.ID, asset[0]) 721 require.NoError(t, err) 722 require.NotNil(t, bondAcc) 723 bondAccBalanceDuringAuction := bondAcc.Balance.Clone() 724 require.True(t, lp.CommitmentAmount.EQ(bondAcc.Balance)) 725 726 insurancePoolAccID := fmt.Sprintf("%s*%s1", tm.market.GetID(), asset[0]) 727 insurancePool, err := tm.collateralEngine.GetAccountByID(insurancePoolAccID) 728 require.NoError(t, err) 729 insurancePoolDuringAuction := insurancePool.Balance.Clone() 730 require.True(t, insurancePool.Balance.IsZero()) 731 732 // End auction 733 setMarkPrice(t, tm, openingAuctionDuration, now, initialMarkPrice) 734 735 mktData = tm.market.GetMarketData() 736 require.NotNil(t, mktData) 737 require.Equal(t, types.MarketTradingModeContinuous, mktData.MarketTradingMode) 738 739 genAcc, err = tm.collateralEngine.GetAccountByID(mainPartyGenAccID) 740 require.NoError(t, err) 741 require.NotNil(t, genAcc) 742 require.True(t, genAcc.Balance.IsZero()) 743 744 bondAcc, err = tm.collateralEngine.GetOrCreatePartyBondAccount(ctx, mainParty, tm.mktCfg.ID, asset[0]) 745 require.NoError(t, err) 746 require.NotNil(t, bondAcc) 747 require.True(t, bondAcc.Balance.LT(bondAccBalanceDuringAuction)) 748 require.False(t, bondAcc.Balance.IsZero()) 749 require.True(t, bondAcc.Balance.LT(lp.CommitmentAmount)) 750 751 insurancePool, err = tm.collateralEngine.GetAccountByID(insurancePoolAccID) 752 require.NotNil(t, insurancePool) 753 require.NoError(t, err) 754 require.True(t, insurancePool.Balance.EQ(insurancePoolDuringAuction)) 755 require.True(t, insurancePool.Balance.IsZero()) 756 }