github.com/dylantkx/matching-engine-core@v0.0.0-20220929025549-6afe3c7e6e9c/engine_test.go (about)

     1  package matchingenginecore_test
     2  
     3  import (
     4  	"fmt"
     5  	"math/rand"
     6  	"sync"
     7  	"testing"
     8  	"time"
     9  
    10  	me "github.com/dylantkx/matching-engine-core"
    11  	"github.com/dylantkx/matching-engine-core/model"
    12  	"github.com/shopspring/decimal"
    13  )
    14  
    15  func TestGetHighestBuyPrice(t *testing.T) {
    16  	engine := me.NewMatchingEngine()
    17  
    18  	p := engine.GetHighestBuyPrice()
    19  	if !p.IsZero() {
    20  		t.Fatalf("expect highest buy to be 0, but got %s", p)
    21  	}
    22  
    23  	ord := model.OrderLimit{
    24  		ID:    "1",
    25  		Units: decimal.NewFromFloat(1),
    26  		Price: decimal.NewFromFloat(100),
    27  		Side:  model.OrderSide_Buy,
    28  	}
    29  	engine.ProcessLimitOrder(&ord)
    30  
    31  	p = engine.GetHighestBuyPrice()
    32  	if !p.Equal(decimal.NewFromFloat(100)) {
    33  		t.Fatalf("expect highest buy to be 100, but got %s", p)
    34  	}
    35  }
    36  
    37  func TestGetLowestSellPrice(t *testing.T) {
    38  	engine := me.NewMatchingEngine()
    39  
    40  	p := engine.GetLowestSellPrice()
    41  	if !p.IsZero() {
    42  		t.Fatalf("expect lowest sell to be 0, but got %s", p)
    43  	}
    44  
    45  	ord := model.OrderLimit{
    46  		ID:    "1",
    47  		Units: decimal.NewFromFloat(1),
    48  		Price: decimal.NewFromFloat(100),
    49  		Side:  model.OrderSide_Sell,
    50  	}
    51  	engine.ProcessLimitOrder(&ord)
    52  
    53  	p = engine.GetLowestSellPrice()
    54  	if !p.Equal(decimal.NewFromFloat(100)) {
    55  		t.Fatalf("expect lowest sell to be 100, but got %s", p)
    56  	}
    57  }
    58  
    59  func TestProcessLimitOneBuyOrder(t *testing.T) {
    60  	engine := me.NewMatchingEngine()
    61  
    62  	ord := model.OrderLimit{
    63  		ID:    "1",
    64  		Units: decimal.NewFromFloat(1),
    65  		Price: decimal.NewFromFloat(100),
    66  		Side:  model.OrderSide_Buy,
    67  	}
    68  	r := engine.ProcessLimitOrder(&ord)
    69  	sn := engine.GetOrderBookFullSnapshot()
    70  
    71  	if len(r.Trades) > 0 {
    72  		t.Fatalf("expect no trades but got %d", len(r.Trades))
    73  	}
    74  	if sn == nil || len(sn.Buys) != 1 {
    75  		t.Fatalf("expect order buy book size = 1 but got %d", len(sn.Buys))
    76  	}
    77  }
    78  
    79  func TestProcessLimitOneSellOrder(t *testing.T) {
    80  	engine := me.NewMatchingEngine()
    81  
    82  	ord := model.OrderLimit{
    83  		ID:    "1",
    84  		Units: decimal.NewFromFloat(1),
    85  		Price: decimal.NewFromFloat(100),
    86  		Side:  model.OrderSide_Sell,
    87  	}
    88  	r := engine.ProcessLimitOrder(&ord)
    89  	sn := engine.GetOrderBookFullSnapshot()
    90  
    91  	if len(r.Trades) > 0 {
    92  		t.Fatalf("expect no trades but got %d", len(r.Trades))
    93  	}
    94  	if sn == nil || len(sn.Sells) != 1 {
    95  		t.Fatalf("expect order book sell size = 1 but got %d", len(sn.Sells))
    96  	}
    97  }
    98  
    99  func TestProcessLimitOrderWithoutTrades(t *testing.T) {
   100  	engine := me.NewMatchingEngine()
   101  
   102  	sellOrders := []model.OrderLimit{
   103  		{
   104  			ID:    "1",
   105  			Units: decimal.NewFromFloat(1),
   106  			Price: decimal.NewFromFloat(100),
   107  			Side:  model.OrderSide_Sell,
   108  		},
   109  		{
   110  			ID:    "2",
   111  			Units: decimal.NewFromFloat(1),
   112  			Price: decimal.NewFromFloat(200),
   113  			Side:  model.OrderSide_Sell,
   114  		},
   115  	}
   116  	for _, ord := range sellOrders {
   117  		engine.ProcessLimitOrder(&ord)
   118  	}
   119  	sn1 := engine.GetOrderBookFullSnapshot()
   120  	if sn1 == nil || len(sn1.Sells) != 2 {
   121  		t.Fatalf("expect order book sell size = 2 but got %d", len(sn1.Sells))
   122  	}
   123  
   124  	buyOrder := model.OrderLimit{
   125  		ID:    "3",
   126  		Units: decimal.NewFromFloat(1),
   127  		Price: decimal.NewFromFloat(50),
   128  		Side:  model.OrderSide_Buy,
   129  	}
   130  	r := engine.ProcessLimitOrder(&buyOrder)
   131  	sn2 := engine.GetOrderBookFullSnapshot()
   132  
   133  	if len(r.Trades) != 0 {
   134  		t.Fatalf("expect 1 trade but got %d", len(r.Trades))
   135  	}
   136  	if sn2 == nil || len(sn2.Buys) != 1 {
   137  		t.Fatalf("expect order buy book size = 1 but got %d", len(sn2.Buys))
   138  	}
   139  }
   140  
   141  func TestLimitBuyOrderProducingTrades(t *testing.T) {
   142  	engine := me.NewMatchingEngine()
   143  
   144  	sellOrders := []model.OrderLimit{
   145  		{
   146  			ID:    "1",
   147  			Units: decimal.NewFromFloat(1),
   148  			Price: decimal.NewFromFloat(100),
   149  			Side:  model.OrderSide_Sell,
   150  		},
   151  		{
   152  			ID:    "2",
   153  			Units: decimal.NewFromFloat(1),
   154  			Price: decimal.NewFromFloat(200),
   155  			Side:  model.OrderSide_Sell,
   156  		},
   157  	}
   158  	for _, ord := range sellOrders {
   159  		engine.ProcessLimitOrder(&ord)
   160  	}
   161  	sn1 := engine.GetOrderBookFullSnapshot()
   162  	if sn1 == nil || len(sn1.Sells) != 2 {
   163  		t.Fatalf("expect order book sell size = 2 but got %d", len(sn1.Sells))
   164  	}
   165  
   166  	buyOrder := model.OrderLimit{
   167  		ID:    "3",
   168  		Units: decimal.NewFromFloat(1.5),
   169  		Price: decimal.NewFromFloat(200),
   170  		Side:  model.OrderSide_Buy,
   171  	}
   172  	r := engine.ProcessLimitOrder(&buyOrder)
   173  	sn2 := engine.GetOrderBookFullSnapshot()
   174  
   175  	if len(r.Trades) != 2 {
   176  		t.Fatalf("expect 2 trade but got %d", len(r.Trades))
   177  	}
   178  	tr := r.Trades[0]
   179  	if tr.BuyOrderID != buyOrder.ID || tr.IsBuyerMaker || tr.Price.GreaterThan(buyOrder.Price) || !tr.Units.Equal(sellOrders[0].Units) {
   180  		t.Fatalf("wrong trade output: %+v", tr)
   181  	}
   182  
   183  	if sn2 == nil || len(sn2.Sells) != 1 {
   184  		t.Fatalf("expect order sell book size = 1 but got %d", len(sn2.Sells))
   185  	}
   186  }
   187  
   188  func TestLimitSellOrderProducingTrades(t *testing.T) {
   189  	engine := me.NewMatchingEngine()
   190  
   191  	buyOrders := []model.OrderLimit{
   192  		{
   193  			ID:    "1",
   194  			Units: decimal.NewFromFloat(1),
   195  			Price: decimal.NewFromFloat(100),
   196  			Side:  model.OrderSide_Buy,
   197  		},
   198  		{
   199  			ID:    "2",
   200  			Units: decimal.NewFromFloat(1),
   201  			Price: decimal.NewFromFloat(200),
   202  			Side:  model.OrderSide_Buy,
   203  		},
   204  	}
   205  	for _, ord := range buyOrders {
   206  		engine.ProcessLimitOrder(&ord)
   207  	}
   208  	sn1 := engine.GetOrderBookFullSnapshot()
   209  	if sn1 == nil || len(sn1.Buys) != 2 {
   210  		t.Fatalf("expect order book buy size = 2 but got %d", len(sn1.Buys))
   211  	}
   212  
   213  	sellOrder := model.OrderLimit{
   214  		ID:    "3",
   215  		Units: decimal.NewFromFloat(2),
   216  		Price: decimal.NewFromFloat(200),
   217  		Side:  model.OrderSide_Sell,
   218  	}
   219  	r := engine.ProcessLimitOrder(&sellOrder)
   220  	sn2 := engine.GetOrderBookFullSnapshot()
   221  
   222  	if len(r.Trades) != 1 {
   223  		t.Fatalf("expect 1 trade but got %d", len(r.Trades))
   224  	}
   225  	tr := r.Trades[0]
   226  	if tr.SellOrderID != sellOrder.ID || !tr.IsBuyerMaker || tr.Price.LessThan(sellOrder.Price) || !tr.Units.Equal(decimal.NewFromFloat(1)) {
   227  		t.Fatalf("wrong trade output: %+v", tr)
   228  	}
   229  
   230  	if sn2 == nil || len(sn2.Sells) != 1 {
   231  		t.Fatalf("expect order sell book size = 1 but got %d", len(sn2.Sells))
   232  	}
   233  	if sn2 == nil || len(sn2.Buys) != 1 {
   234  		t.Fatalf("expect order buy book size = 1 but got %d", len(sn2.Buys))
   235  	}
   236  }
   237  
   238  func TestProcessMarketOneBuyOrder(t *testing.T) {
   239  	engine := me.NewMatchingEngine()
   240  
   241  	ord := model.OrderMarket{
   242  		ID:    "1",
   243  		Units: decimal.NewFromFloat(1),
   244  		Side:  model.OrderSide_Buy,
   245  	}
   246  	r := engine.ProcessMarketOrder(&ord)
   247  
   248  	if len(r.Trades) > 0 {
   249  		t.Fatalf("expect no trades but got %d", len(r.Trades))
   250  	}
   251  	if len(r.Cancellations) != 1 {
   252  		t.Fatalf("expect 1 cancellation but got %d", len(r.Cancellations))
   253  	}
   254  	if !r.Cancellations[0].Units.Equal(ord.Units) {
   255  		t.Fatalf("expect to cancel whole order, but got %s", r.Cancellations[0].Units)
   256  	}
   257  }
   258  
   259  func TestProcessMarketOneSellOrder(t *testing.T) {
   260  	engine := me.NewMatchingEngine()
   261  
   262  	ord := model.OrderMarket{
   263  		ID:    "1",
   264  		Units: decimal.NewFromFloat(1),
   265  		Side:  model.OrderSide_Sell,
   266  	}
   267  	r := engine.ProcessMarketOrder(&ord)
   268  
   269  	if len(r.Trades) > 0 {
   270  		t.Fatalf("expect no trades but got %d", len(r.Trades))
   271  	}
   272  	if len(r.Cancellations) != 1 {
   273  		t.Fatalf("expect 1 cancellation but got %d", len(r.Cancellations))
   274  	}
   275  	if !r.Cancellations[0].Units.Equal(ord.Units) {
   276  		t.Fatalf("expect to cancel whole order, but got %s", r.Cancellations[0].Units)
   277  	}
   278  }
   279  
   280  func TestMarketBuyOrderProducingTrades(t *testing.T) {
   281  	engine := me.NewMatchingEngine()
   282  
   283  	sellOrders := []model.OrderLimit{
   284  		{
   285  			ID:    "1",
   286  			Units: decimal.NewFromFloat(1),
   287  			Price: decimal.NewFromFloat(100),
   288  			Side:  model.OrderSide_Sell,
   289  		},
   290  		{
   291  			ID:    "2",
   292  			Units: decimal.NewFromFloat(1),
   293  			Price: decimal.NewFromFloat(200),
   294  			Side:  model.OrderSide_Sell,
   295  		},
   296  	}
   297  	for _, ord := range sellOrders {
   298  		engine.ProcessLimitOrder(&ord)
   299  	}
   300  
   301  	buyOrder := model.OrderMarket{
   302  		ID:    "3",
   303  		Units: decimal.NewFromFloat(1.5),
   304  		Side:  model.OrderSide_Buy,
   305  	}
   306  	r := engine.ProcessMarketOrder(&buyOrder)
   307  	sn2 := engine.GetOrderBookFullSnapshot()
   308  
   309  	if len(r.Trades) != 2 {
   310  		t.Fatalf("expect 2 trades but got %d", len(r.Trades))
   311  	}
   312  	if len(r.Cancellations) != 0 {
   313  		t.Fatalf("expect 0 cancels but got %d", len(r.Cancellations))
   314  	}
   315  	tr1, tr2 := r.Trades[0], r.Trades[1]
   316  	if tr1.BuyOrderID != buyOrder.ID || tr1.IsBuyerMaker || !tr1.Price.Equal(sellOrders[0].Price) || !tr1.Units.Equal(sellOrders[0].Units) {
   317  		t.Fatalf("wrong trade output: %+v", tr1)
   318  	}
   319  	if tr2.BuyOrderID != buyOrder.ID || tr2.IsBuyerMaker || !tr2.Price.Equal(sellOrders[1].Price) || !tr2.Units.Equal(buyOrder.Units.Sub(sellOrders[0].Units)) {
   320  		t.Fatalf("wrong trade output: %+v", tr2)
   321  	}
   322  
   323  	if sn2 == nil || len(sn2.Sells) != 1 {
   324  		t.Fatalf("expect order sell book size = 1 but got %d", len(sn2.Sells))
   325  	}
   326  }
   327  
   328  func TestMarketSellOrderProducingTrades(t *testing.T) {
   329  	engine := me.NewMatchingEngine()
   330  
   331  	buyOrders := []model.OrderLimit{
   332  		{
   333  			ID:    "1",
   334  			Units: decimal.NewFromFloat(1),
   335  			Price: decimal.NewFromFloat(200),
   336  			Side:  model.OrderSide_Buy,
   337  		},
   338  		{
   339  			ID:    "2",
   340  			Units: decimal.NewFromFloat(1),
   341  			Price: decimal.NewFromFloat(100),
   342  			Side:  model.OrderSide_Buy,
   343  		},
   344  	}
   345  	for _, ord := range buyOrders {
   346  		engine.ProcessLimitOrder(&ord)
   347  	}
   348  
   349  	sellOrder := model.OrderMarket{
   350  		ID:    "3",
   351  		Units: decimal.NewFromFloat(1.5),
   352  		Side:  model.OrderSide_Sell,
   353  	}
   354  	r := engine.ProcessMarketOrder(&sellOrder)
   355  	sn2 := engine.GetOrderBookFullSnapshot()
   356  
   357  	if len(r.Trades) != 2 {
   358  		t.Fatalf("expect 2 trade but got %d", len(r.Trades))
   359  	}
   360  	if len(r.Cancellations) != 0 {
   361  		t.Fatalf("expect 0 cancels but got %d", len(r.Cancellations))
   362  	}
   363  	tr1, tr2 := r.Trades[0], r.Trades[1]
   364  	if tr1.SellOrderID != sellOrder.ID || !tr1.IsBuyerMaker || !tr1.Price.Equal(buyOrders[0].Price) || !tr1.Units.Equal(buyOrders[0].Units) {
   365  		t.Fatalf("wrong trade output: %+v", tr1)
   366  	}
   367  	if tr2.SellOrderID != sellOrder.ID || !tr2.IsBuyerMaker || !tr2.Price.Equal(buyOrders[1].Price) || !tr2.Units.Equal(sellOrder.Units.Sub(buyOrders[0].Units)) {
   368  		t.Fatalf("wrong trade output: %+v", tr2)
   369  	}
   370  
   371  	if sn2 == nil || len(sn2.Buys) != 1 {
   372  		t.Fatalf("expect order buy book size = 1 but got %d", len(sn2.Buys))
   373  	}
   374  }
   375  
   376  func TestMarketBuyOrderProducingTradesWithCancels(t *testing.T) {
   377  	engine := me.NewMatchingEngine()
   378  
   379  	sellOrders := []model.OrderLimit{
   380  		{
   381  			ID:    "1",
   382  			Units: decimal.NewFromFloat(1),
   383  			Price: decimal.NewFromFloat(100),
   384  			Side:  model.OrderSide_Sell,
   385  		},
   386  		{
   387  			ID:    "2",
   388  			Units: decimal.NewFromFloat(1),
   389  			Price: decimal.NewFromFloat(200),
   390  			Side:  model.OrderSide_Sell,
   391  		},
   392  	}
   393  	for _, ord := range sellOrders {
   394  		engine.ProcessLimitOrder(&ord)
   395  	}
   396  
   397  	buyOrder := model.OrderMarket{
   398  		ID:    "3",
   399  		Units: decimal.NewFromFloat(2.5),
   400  		Side:  model.OrderSide_Buy,
   401  	}
   402  	r := engine.ProcessMarketOrder(&buyOrder)
   403  	sn2 := engine.GetOrderBookFullSnapshot()
   404  
   405  	if len(r.Trades) != 2 {
   406  		t.Fatalf("expect 2 trade but got %d", len(r.Trades))
   407  	}
   408  	if len(r.Cancellations) != 1 {
   409  		t.Fatalf("expect 1 cancels but got %d", len(r.Cancellations))
   410  	}
   411  	tr1, tr2 := r.Trades[0], r.Trades[1]
   412  	if tr1.BuyOrderID != buyOrder.ID || tr1.IsBuyerMaker || !tr1.Price.Equal(sellOrders[0].Price) || !tr1.Units.Equal(sellOrders[0].Units) {
   413  		t.Fatalf("wrong trade output: %+v", tr1)
   414  	}
   415  	if tr2.BuyOrderID != buyOrder.ID || tr2.IsBuyerMaker || !tr2.Price.Equal(sellOrders[1].Price) || !tr2.Units.Equal(sellOrders[0].Units) {
   416  		t.Fatalf("wrong trade output: %+v", tr2)
   417  	}
   418  	if !r.Cancellations[0].Units.Equal(buyOrder.Units.Sub(sellOrders[0].Units.Add(sellOrders[1].Units))) {
   419  		t.Fatalf("wrong cancelled units, got %s", r.Cancellations[0].Units)
   420  	}
   421  
   422  	if sn2 == nil || len(sn2.Sells) != 0 {
   423  		fmt.Printf("%+v\n", sn2.Sells)
   424  		t.Fatalf("expect order sell book size = 0 but got %d", len(sn2.Sells))
   425  	}
   426  }
   427  
   428  func TestNearlyConcurrentMarketBuys(t *testing.T) {
   429  	engine := me.NewMatchingEngine()
   430  
   431  	sellOrders := []model.OrderLimit{
   432  		{
   433  			ID:    "1",
   434  			Units: decimal.NewFromFloat(1),
   435  			Price: decimal.NewFromFloat(100),
   436  			Side:  model.OrderSide_Sell,
   437  		},
   438  		{
   439  			ID:    "2",
   440  			Units: decimal.NewFromFloat(1),
   441  			Price: decimal.NewFromFloat(200),
   442  			Side:  model.OrderSide_Sell,
   443  		},
   444  	}
   445  	for _, ord := range sellOrders {
   446  		engine.ProcessLimitOrder(&ord)
   447  	}
   448  
   449  	buyOrder1 := model.OrderMarket{
   450  		ID:    "3",
   451  		Units: decimal.NewFromFloat(1),
   452  		Side:  model.OrderSide_Buy,
   453  	}
   454  	buyOrder2 := model.OrderMarket{
   455  		ID:    "4",
   456  		Units: decimal.NewFromFloat(1),
   457  		Side:  model.OrderSide_Buy,
   458  	}
   459  
   460  	var trades_1, trades_2 []model.Trade
   461  
   462  	wg := sync.WaitGroup{}
   463  	wg.Add(2)
   464  
   465  	go func() {
   466  		defer wg.Done()
   467  		r := engine.ProcessMarketOrder(&buyOrder1)
   468  		trades_1 = r.Trades
   469  	}()
   470  	go func() {
   471  		time.Sleep(time.Millisecond * 1)
   472  		defer wg.Done()
   473  		r := engine.ProcessMarketOrder(&buyOrder2)
   474  		trades_2 = r.Trades
   475  	}()
   476  
   477  	wg.Wait()
   478  
   479  	if len(trades_1) != 1 {
   480  		t.Fatalf("expect 1 trade but got %d", len(trades_1))
   481  	}
   482  
   483  	if !trades_1[0].Price.Equal(sellOrders[0].Price) {
   484  		t.Fatalf("expect trade 1 to be filled with price of %s, but got %s", sellOrders[0].Price, trades_1[0].Price)
   485  	}
   486  	if !trades_2[0].Price.Equal(sellOrders[1].Price) {
   487  		t.Fatalf("expect trade 2 to be filled with price of %s, but got %s", sellOrders[1].Price, trades_2[0].Price)
   488  	}
   489  }
   490  
   491  func TestCancelOrder(t *testing.T) {
   492  	engine := me.NewMatchingEngine()
   493  
   494  	ord := model.OrderLimit{
   495  		ID:    "1",
   496  		Units: decimal.NewFromFloat(1),
   497  		Price: decimal.NewFromFloat(100),
   498  		Side:  model.OrderSide_Buy,
   499  	}
   500  	engine.ProcessLimitOrder(&ord)
   501  
   502  	cancels, err := engine.CancelOrder(model.Order{
   503  		ID:    ord.ID,
   504  		Units: ord.Units.Copy(),
   505  		Price: ord.Price.Copy(),
   506  		Side:  ord.Side,
   507  	})
   508  	if err != nil {
   509  		t.Fatalf(err.Error())
   510  	}
   511  
   512  	sn := engine.GetOrderBookFullSnapshot()
   513  
   514  	if len(cancels) != 1 {
   515  		t.Fatalf("expect 1 cancel but got %d", len(cancels))
   516  	}
   517  	if cancels[0].OrderID != ord.ID || !cancels[0].Units.Equal(ord.Units) {
   518  		t.Fatalf("wrong cancel output: %+v", cancels)
   519  	}
   520  	if sn == nil || len(sn.Buys) != 0 {
   521  		t.Fatalf("expect order buy book size = 0 but got %d", len(sn.Buys))
   522  	}
   523  }
   524  
   525  func BenchmarkProcessLimitOrders(b *testing.B) {
   526  	b.StopTimer()
   527  	engine := me.NewMatchingEngine()
   528  	orders := make([]*model.OrderLimit, 0, b.N)
   529  	for i := 0; i < b.N; i++ {
   530  		order := &model.OrderLimit{
   531  			ID:    fmt.Sprintf("%d", i+1),
   532  			Units: decimal.NewFromFloat((rand.Float64() + float64(rand.Intn(2))) * 10),
   533  			Price: decimal.NewFromFloat((rand.Float64() + float64(rand.Intn(2))) * 100),
   534  			Side:  model.OrderSide_Buy,
   535  		}
   536  		orders = append(orders, order)
   537  	}
   538  	b.StartTimer()
   539  	for _, order := range orders {
   540  		engine.ProcessLimitOrder(order)
   541  	}
   542  }
   543  
   544  func BenchmarkProcessLimitOrdersAsync(b *testing.B) {
   545  	b.StopTimer()
   546  	engine := me.NewMatchingEngine()
   547  	orders := make([]*model.OrderLimit, 0, b.N)
   548  	for i := 0; i < b.N; i++ {
   549  		order := &model.OrderLimit{
   550  			ID:    fmt.Sprintf("%d", i+1),
   551  			Units: decimal.NewFromFloat((rand.Float64() + float64(rand.Intn(2))) * 10),
   552  			Price: decimal.NewFromFloat((rand.Float64() + float64(rand.Intn(2))) * 100),
   553  			Side:  model.OrderSide_Buy,
   554  		}
   555  		orders = append(orders, order)
   556  	}
   557  	b.StartTimer()
   558  	wg := sync.WaitGroup{}
   559  	for _, order := range orders {
   560  		wg.Add(1)
   561  		go func(order *model.OrderLimit) {
   562  			defer wg.Done()
   563  			engine.ProcessLimitOrder(order)
   564  		}(order)
   565  	}
   566  	wg.Wait()
   567  }
   568  
   569  func BenchmarkProcessOneMarketOrderProducingTrades(b *testing.B) {
   570  	b.StopTimer()
   571  	engine := me.NewMatchingEngine()
   572  	var n int = 1e5
   573  	orders := make([]*model.OrderLimit, 0, n)
   574  	for i := 0; i < n; i++ {
   575  		order := &model.OrderLimit{
   576  			ID:    fmt.Sprintf("%d", i+1),
   577  			Units: decimal.NewFromFloat((rand.Float64() + float64(rand.Intn(2))) * 10),
   578  			Price: decimal.NewFromFloat((rand.Float64() + float64(rand.Intn(2))) * 100),
   579  			Side:  model.OrderSide_Buy,
   580  		}
   581  		orders = append(orders, order)
   582  	}
   583  	for _, order := range orders {
   584  		engine.ProcessLimitOrder(order)
   585  	}
   586  
   587  	order := &model.OrderMarket{
   588  		ID:    fmt.Sprintf("%d", n+1),
   589  		Units: decimal.NewFromFloat(100),
   590  		Side:  model.OrderSide_Sell,
   591  	}
   592  
   593  	b.StartTimer()
   594  	engine.ProcessMarketOrder(order)
   595  }