package orderbook import ( "errors" "testing" "time" "github.com/gofrs/uuid" ) var unsafeID, _ = uuid.NewV4() type externalBook struct{} func (e *externalBook) Lock() {} func (e *externalBook) Unlock() {} func TestUnsafe(t *testing.T) { t.Parallel() d := NewDepth(unsafeID) ob := d.GetUnsafe() if ob.AskHead == nil || ob.BidHead == nil || ob.m == nil { t.Fatal("these items should not be nil") } ob2 := &externalBook{} ob.Lock() ob.Unlock() //nolint:staticcheck, gocritic // Not needed in test ob.LockWith(ob2) ob.UnlockWith(ob2) } func TestGetLiquidity(t *testing.T) { t.Parallel() d := NewDepth(unsafeID) unsafe := d.GetUnsafe() _, _, err := unsafe.GetLiquidity() if !errors.Is(err, errNoLiquidity) { t.Fatalf("received: '%v' but expected: '%v'", err, errNoLiquidity) } d.LoadSnapshot([]Item{{Price: 2}}, nil, 0, time.Time{}, false) _, _, err = unsafe.GetLiquidity() if !errors.Is(err, errNoLiquidity) { t.Fatalf("received: '%v' but expected: '%v'", err, errNoLiquidity) } d.LoadSnapshot([]Item{{Price: 2}}, []Item{{Price: 2}}, 0, time.Time{}, false) aN, bN, err := unsafe.GetLiquidity() if !errors.Is(err, nil) { t.Fatalf("received: '%v' but expected: '%v'", err, nil) } if aN == nil { t.Fatal("unexpected value") } if bN == nil { t.Fatal("unexpected value") } } func TestCheckBidLiquidity(t *testing.T) { t.Parallel() d := NewDepth(unsafeID) unsafe := d.GetUnsafe() err := unsafe.CheckBidLiquidity() if !errors.Is(err, errNoLiquidity) { t.Fatalf("received: '%v' but expected: '%v'", err, errNoLiquidity) } d.LoadSnapshot([]Item{{Price: 2}}, nil, 0, time.Time{}, false) err = unsafe.CheckBidLiquidity() if !errors.Is(err, nil) { t.Fatalf("received: '%v' but expected: '%v'", err, nil) } } func TestCheckAskLiquidity(t *testing.T) { t.Parallel() d := NewDepth(unsafeID) unsafe := d.GetUnsafe() err := unsafe.CheckAskLiquidity() if !errors.Is(err, errNoLiquidity) { t.Fatalf("received: '%v' but expected: '%v'", err, errNoLiquidity) } d.LoadSnapshot(nil, []Item{{Price: 2}}, 0, time.Time{}, false) err = unsafe.CheckAskLiquidity() if !errors.Is(err, nil) { t.Fatalf("received: '%v' but expected: '%v'", err, nil) } } func TestGetBestBid(t *testing.T) { t.Parallel() d := NewDepth(unsafeID) unsafe := d.GetUnsafe() if _, err := unsafe.GetBestBid(); !errors.Is(err, errNoLiquidity) { t.Fatalf("received: '%v' but expected: '%v'", err, errNoLiquidity) } d.LoadSnapshot([]Item{{Price: 2}}, nil, 0, time.Time{}, false) bestBid, err := unsafe.GetBestBid() if !errors.Is(err, nil) { t.Fatalf("received: '%v' but expected: '%v'", err, nil) } if bestBid != 2 { t.Fatal("unexpected value") } } func TestGetBestAsk(t *testing.T) { t.Parallel() d := NewDepth(unsafeID) unsafe := d.GetUnsafe() if _, err := unsafe.GetBestAsk(); !errors.Is(err, errNoLiquidity) { t.Fatalf("received: '%v' but expected: '%v'", err, errNoLiquidity) } d.LoadSnapshot(nil, []Item{{Price: 2}}, 0, time.Time{}, false) bestAsk, err := unsafe.GetBestAsk() if !errors.Is(err, nil) { t.Fatalf("received: '%v' but expected: '%v'", err, nil) } if bestAsk != 2 { t.Fatal("unexpected value") } } func TestGetMidPrice(t *testing.T) { t.Parallel() d := NewDepth(unsafeID) unsafe := d.GetUnsafe() if _, err := unsafe.GetMidPrice(); !errors.Is(err, errNoLiquidity) { t.Fatalf("received: '%v' but expected: '%v'", err, errNoLiquidity) } d.LoadSnapshot([]Item{{Price: 1}}, []Item{{Price: 2}}, 0, time.Time{}, false) mid, err := unsafe.GetMidPrice() if !errors.Is(err, nil) { t.Fatalf("received: '%v' but expected: '%v'", err, nil) } if mid != 1.5 { t.Fatal("unexpected value") } } func TestGetSpread(t *testing.T) { t.Parallel() d := NewDepth(unsafeID) unsafe := d.GetUnsafe() if _, err := unsafe.GetSpread(); !errors.Is(err, errNoLiquidity) { t.Fatalf("received: '%v' but expected: '%v'", err, errNoLiquidity) } d.LoadSnapshot([]Item{{Price: 1}}, []Item{{Price: 2}}, 0, time.Time{}, false) spread, err := unsafe.GetSpread() if !errors.Is(err, nil) { t.Fatalf("received: '%v' but expected: '%v'", err, nil) } if spread != 1 { t.Fatal("unexpected value") } } func TestGetImbalance(t *testing.T) { t.Parallel() d := NewDepth(unsafeID) unsafe := d.GetUnsafe() _, err := unsafe.GetImbalance() if !errors.Is(err, errNoLiquidity) { t.Fatalf("received: '%v' but expected: '%v'", err, errNoLiquidity) } // unlikely event zero amounts d.LoadSnapshot([]Item{{Price: 1, Amount: 0}}, []Item{{Price: 2, Amount: 0}}, 0, time.Time{}, false) _, err = unsafe.GetImbalance() if !errors.Is(err, errNoLiquidity) { t.Fatalf("received: '%v' but expected: '%v'", err, errNoLiquidity) } // balance skewed to asks d.LoadSnapshot([]Item{{Price: 1, Amount: 1}}, []Item{{Price: 2, Amount: 1000}}, 0, time.Time{}, false) imbalance, err := unsafe.GetImbalance() if !errors.Is(err, nil) { t.Fatalf("received: '%v' but expected: '%v'", err, nil) } if imbalance != -0.998001998001998 { t.Fatal("unexpected value") } // balance skewed to bids d.LoadSnapshot([]Item{{Price: 1, Amount: 1000}}, []Item{{Price: 2, Amount: 1}}, 0, time.Time{}, false) imbalance, err = unsafe.GetImbalance() if !errors.Is(err, nil) { t.Fatalf("received: '%v' but expected: '%v'", err, nil) } if imbalance != 0.998001998001998 { t.Fatal("unexpected value") } // in balance d.LoadSnapshot([]Item{{Price: 1, Amount: 1}}, []Item{{Price: 2, Amount: 1}}, 0, time.Time{}, false) imbalance, err = unsafe.GetImbalance() if !errors.Is(err, nil) { t.Fatalf("received: '%v' but expected: '%v'", err, nil) } if imbalance != 0 { t.Fatal("unexpected value") } } func TestIsStreaming(t *testing.T) { d := NewDepth(unsafeID) unsafe := d.GetUnsafe() if !unsafe.IsStreaming() { t.Fatalf("received: '%v' but expected: '%v'", unsafe.IsStreaming(), true) } d.LoadSnapshot([]Item{{Price: 1, Amount: 1}}, []Item{{Price: 2, Amount: 1}}, 0, time.Time{}, true) if unsafe.IsStreaming() { t.Fatalf("received: '%v' but expected: '%v'", unsafe.IsStreaming(), false) } d.LoadSnapshot([]Item{{Price: 1, Amount: 1}}, []Item{{Price: 2, Amount: 1}}, 0, time.Time{}, false) if !unsafe.IsStreaming() { t.Fatalf("received: '%v' but expected: '%v'", unsafe.IsStreaming(), true) } }