module TestLOB ( tests ) where import ATrade.ESim.Core (MatchingAction (..), PriceTick (PriceTick), Volume (..), addToLob, emptyLob, executeMatchingActions) import ATrade.Types (Operation (..), Order (..), OrderPrice (..), OrderState (..), Price, SignalId (..)) import Hedgehog import Hedgehog.Gen qualified as Gen import Hedgehog.Range qualified as Range import Test.Tasty import Test.Tasty.Hedgehog tests :: TestTree tests = testGroup "LimitOrderBook" [ testProperty "add to empty LOB => enqueues order" prop_add_to_empty_lob, testProperty "full match" prop_full_match ] prop_add_to_empty_lob :: Property prop_add_to_empty_lob = property $ do priceTick <- forAll $ Gen.integral (Range.linear 1 100000) order <- forAll $ Order <$> Gen.integral (Range.linear 1 100000) <*> Gen.text (Range.linear 1 50) Gen.unicode <*> Gen.text (Range.linear 1 50) Gen.unicode <*> pure (Limit $ fromInteger priceTick * tickSize) <*> Gen.integral (Range.linear 1 10000) <*> pure 0 <*> pure Buy <*> pure Unsubmitted <*> pure (SignalId "test" "foo" "") addToLob order lob === [Enqueue (orderId order) (PriceTick . fromInteger $ priceTick) (Volume . fromIntegral $ orderQuantity order) (orderOperation order)] where lob = emptyLob "Test" tickSize tickSize :: Price tickSize = 0.1 prop_full_match :: Property prop_full_match = property $ do priceTick <- forAll $ Gen.integral (Range.linear 1 100000) order <- forAll $ Order <$> Gen.integral (Range.linear 1 100000) <*> Gen.text (Range.linear 1 50) Gen.unicode <*> Gen.text (Range.linear 1 50) Gen.unicode <*> pure Market <*> Gen.integral (Range.linear 1 10000) <*> pure 0 <*> pure Buy <*> pure Unsubmitted <*> pure (SignalId "test" "foo" "") addToLob order (lob order priceTick) === [Match (orderId order) 1 (orderOperation order) (PriceTick priceTick) (Volume . fromIntegral $ orderQuantity order)] where lob order priceTick = executeMatchingActions lob0 [Enqueue 1 (PriceTick priceTick) (Volume . fromIntegral $ orderQuantity order) Sell] lob0 = emptyLob "Test" tickSize tickSize = 0.1