{-# LANGUAGE OverloadedRecordDot #-} {-# LANGUAGE QuasiQuotes #-} module Test.Transaq.Parsing ( unitTests ) where import Control.Monad.State (execState) import Data.AEq ((~==)) import qualified Data.ByteString as BS import Test.Tasty (TestTree, testGroup) import Test.Tasty.HUnit (assertBool, assertFailure, testCase, (@=?)) import Text.RawString.QQ import Transaq (AllTradesTradeB (..), CandleB (..), CandleKind, CandleKindB (..), ClientDataB (..), ConnectionState (..), MarketInfoB (..), OrderNotificationB (..), OrderStatus (..), QuotationB (..), QuoteB (..), ResponseAllTrades (..), ResponseCandleKinds (..), ResponseCandlesB (..), ResponseCandlesStatus (..), ResponseClient (..), ResponseMarkets (..), ResponseOrders (..), ResponseQuotations (..), ResponseQuotes (..), ResponseResult (..), ResponseSecInfoB (..), ResponseSecurities (..), ResponseServerStatusB (..), ResponseTrades (..), SecurityB (..), SecurityId (..), TradeDirection (..), TradeNotificationB (..), TradingPeriod (..), TransaqResponse (..)) import Transaq.Parsing import Xeno.SAX (process) unitTests :: TestTree unitTests = testGroup "Parsing" [ unitTestsStreaming ] unitTestsStreaming :: TestTree unitTestsStreaming = testGroup "Parsing via xeno-sax" [ testParseResultStreaming , testParseClientStreaming , testParseCandleKindsStreaming , testParseCandlesStreaming , testParseServerStatusStreaming , testParseMarketsStreaming , testParseSecuritiesStreaming , testParseSecInfoStreaming , testParseQuotationsStreaming , testParseAllTradesStreaming , testParseQuotesStreaming , testParseOrdersStreaming , testParseTradesStreaming ] testParseResultStreaming :: TestTree testParseResultStreaming = testGroup "Parsing result" [ testParseResultSuccess , testParseResultSuccessWithTransactionId , testParseResultFailure ] where resultSuccess = [r||] :: BS.ByteString testParseResultSuccess = testCase "Parse success result" $ do [TransaqResponseResult (ResponseSuccess Nothing)] @=? parseTransaqResponses resultSuccess resultSuccessWithTransactionId = [r||] :: BS.ByteString testParseResultSuccessWithTransactionId = testCase "Parse success result with transaction ID" $ do [TransaqResponseResult (ResponseSuccess (Just 12))] @=? parseTransaqResponses resultSuccessWithTransactionId resultFailure = [r||] :: BS.ByteString testParseResultFailure = testCase "Parse failure result" $ do [TransaqResponseResult (ResponseFailure "")] @=? parseTransaqResponses resultFailure testParseClientStreaming :: TestTree testParseClientStreaming = testCase "Parse ResponseClient - valid XML" $ do case parseTransaqResponses validClient of [TransaqResponseClient (ResponseClient clientData)] -> do "FOO" @=? cClientId clientData "TYPE" @=? cType clientData "CURRENCY" @=? cCurrency clientData "MARKET" @=? cMarket clientData "UNION" @=? cUnion clientData Just "FORTS_ACC" @=? cForts clientData _ -> assertFailure "Invalid parse" where validClient = [r| TYPE CURRENCY MARKET UNION FORTS_ACC |] :: BS.ByteString testParseCandleKindsStreaming :: TestTree testParseCandleKindsStreaming = testCase "Parse CandleKind - valid XML" $ do let candleKinds = parseTransaqResponses validCandleKinds let [TransaqResponseCandleKinds (ResponseCandleKinds [m10, m15])] = candleKinds 4 @=? kCandleKindId m10 600 @=? kPeriod m10 "M10" @=? kName m10 5 @=? kCandleKindId m15 900 @=? kPeriod m15 "M15" @=? kName m15 where validCandleKinds = [r| 4 600 M10 5 900 M15 |] :: BS.ByteString testParseCandlesStreaming :: TestTree testParseCandlesStreaming = testCase "Parse Candles - valid XML" $ do let [TransaqResponseCandles responseCandles] = parseTransaqResponses validCandles SecurityId "foo" "bar" @=? cSecurity responseCandles 1 @=? cPeriodId responseCandles StatusEndOfHistory @=? cStatus responseCandles 1 @=? (length . cCandles) responseCandles let candle = head . cCandles $ responseCandles assertBool "Candle open mismatch" (41.3 ~== cOpen candle) assertBool "Candle high mismatch" (42 ~== cHigh candle) assertBool "Candle low mismatch" (40 ~== cLow candle) assertBool "Candle close mismatch" (41.6 ~== cClose candle) 1234 @=? cVolume candle 78 @=? cOpenInterest candle where validCandles = [r| |] :: BS.ByteString testParseServerStatusStreaming :: TestTree testParseServerStatusStreaming = testCase "Parse ServerStatus - valid XML" $ do let [TransaqResponseServerStatus serverStatus] = parseTransaqResponses validServerStatus Just 22 @=? serverId serverStatus Connected @=? state serverStatus Just True @=? recover serverStatus Just "TZ" @=? serverTimezone serverStatus Just 42 @=? systemVersion serverStatus Just 51 @=? build serverStatus where validServerStatus = [r| |] :: BS.ByteString testParseMarketsStreaming :: TestTree testParseMarketsStreaming = testCase "Parse ResponseMarkets - valid XML" $ do let markets = parseTransaqResponses validMarkets let [TransaqResponseMarkets (ResponseMarkets [foo, bar])] = markets 1 @=? marketId foo "FOO" @=? marketName foo 2 @=? marketId bar "BAR" @=? marketName bar where validMarkets = [r| FOO BAR |] :: BS.ByteString testParseSecuritiesStreaming :: TestTree testParseSecuritiesStreaming = testCase "Parse ResponseSecurities - valid XML" $ do let [TransaqResponseSecurities (ResponseSecurities securities)] = parseTransaqResponses validSecurities 1 @=? length securities let sec = head securities "SECCODE" @=? sSeccode sec "CLASS" @=? sInstrClass sec "BOARD" @=? sBoard sec "15" @=? sMarket sec "CURRENCY" @=? sCurrency sec "SHORTNAME" @=? sShortName sec 3 @=? sDecimals sec assertBool "Minstep is not valid" (0.1 ~== sMinStep sec) 10 @=? sLotSize sec 1 @=? sLotDivider sec assertBool "Point cost is not valid" (6.28 ~== sPointCost sec) "SECTYPE" @=? sSecType sec where validSecurities = [r| SECCODE CLASS BOARD 15 CURRENCY SHORTNAME 3 0.1 10 1 6.28 SECTYPE SECTZ 1 FOO CURRENCYID |] :: BS.ByteString testParseSecInfoStreaming :: TestTree testParseSecInfoStreaming = testCase "Parse ResponseSecInfo - valid XML" $ do let [TransaqResponseSecInfo sec] = parseTransaqResponses validSecInfo "SECNAME" @=? secName sec "SECCODE" @=? sec.secCode 44 @=? market sec "PNAME" @=? pname sec assertBool "Clearing price is not valid" (12.34 ~== clearingPrice sec) assertBool "Min price is not valid" (10 ~== minPrice sec) assertBool "Max price is not valid" (20 ~== maxPrice sec) assertBool "Point cost is not valid" (6.28 ~== pointCost sec) where validSecInfo = [r| SECNAME SECCODE 44 PNAME 12.34 10 20 6.28 |] :: BS.ByteString testParseQuotationsStreaming :: TestTree testParseQuotationsStreaming = testCase "Parse ResponseQuotations - valid XML (full)" $ do let [TransaqResponseQuotations (ResponseQuotations quotations)] = parseTransaqResponses validQuotations 1 @=? length quotations let q = head quotations 1 @=? qSecId q "BOARD" @=? qBoard q "SECCODE" @=? qSeccode q let (Just open') = qOpen q assertBool "Open is not valid" (12.34 ~== open') let (Just waprice') = qWaprice q assertBool "WA price is not valid" (13.8 ~== waprice') Just 40 @=? qBidDepth q Just 140 @=? qBidDepthT q Just 10 @=? qNumBids q let (Just bid') = qBid q assertBool "Bid is not valid" (11.01 ~== bid') Just 50 @=? qOfferDepth q Just 150 @=? qOfferDepthT q Just 15 @=? qNumOffers q let (Just offer') = qOffer q assertBool "Offer is not valid" (11.05 ~== offer') Just 1000 @=? qNumTrades q Just 50000 @=? qVolToday q Just 1000 @=? qOpenPositions q let (Just last') = qLastPrice q assertBool "Last price is not valid" (11.03 ~== last') Just 8 @=? qQuantity q -- TODO check time let (Just valtoday') = qValToday q assertBool "Val today is not valid" (12345678 ~== valtoday') where validQuotations = [r| BOARD SECCODE 12.34 13.8 40 140 10 11.01 50 150 15 11.05 1000 50000 1000 11.03 8 12345678 |] :: BS.ByteString testParseAllTradesStreaming :: TestTree testParseAllTradesStreaming = testCase "Parse ResponseAllTrades - valid XML (full)" $ do let [TransaqResponseAllTrades (ResponseAllTrades trades)] = parseTransaqResponses validAllTrades 2 @=? length trades let [t1, t2] = trades 1 @=? attSecId t1 "SEC1" @=? attSecCode t1 14 @=? attTradeNo t1 -- TODO check time "BOARD" @=? attBoard t1 assertBool "Price is not valid" (12.34 ~== attPrice t1) 10 @=? attQuantity t1 Buy @=? attBuysell t1 100 @=? attOpenInterest t1 PeriodNormal @=? attPeriod t1 2 @=? attSecId t2 "SEC2" @=? attSecCode t2 15 @=? attTradeNo t2 -- TODO check time "BOARD" @=? attBoard t2 assertBool "Price is not valid" (12.35 ~== attPrice t2) 11 @=? attQuantity t2 Sell @=? attBuysell t2 200 @=? attOpenInterest t2 PeriodNormal @=? attPeriod t2 where validAllTrades = [r| SEC1 14 BOARD 12.34 10 B 100 N SEC2 15 BOARD 12.35 11 S 200 N |] :: BS.ByteString testParseQuotesStreaming :: TestTree testParseQuotesStreaming = testCase "Parse ResponseQuotes - valid XML (full)" $ do let [TransaqResponseQuotes (ResponseQuotes quotes)] = parseTransaqResponses validQuotes 2 @=? length quotes let [q1, q2] = quotes 1 @=? q1.secId "BOARD" @=? q1.board assertBool "Price is not valid" (12.34 ~== q1.price) Just "SOURCE" @=? q1.source Just 10 @=? q1.buy Nothing @=? q1.sell 2 @=? q2.secId "BOARD" @=? q2.board assertBool "Price is not valid" (12.35 ~== q2.price) Just "SOURCE" @=? q2.source Nothing @=? q2.buy Just 11 @=? q2.sell where validQuotes = [r| BOARD SEC1 12.34 SOURCE 10 BOARD SEC2 12.35 SOURCE 11 |] :: BS.ByteString testParseOrdersStreaming :: TestTree testParseOrdersStreaming = testCase "Parse ResponseOrders - valid XML (full)" $ do let [TransaqResponseOrders (ResponseOrders orders)] = parseTransaqResponses validOrders 1 @=? length orders let order = head orders 12 @=? oTransactionId order 42 @=? oOrderNo order 1 @=? oSecId order "BOARD" @=? oBoard order "SEC1" @=? oSecCode order Just "CLIENT" @=? oClient order Just "UNION" @=? oUnion order Just OrderActive @=? oStatus order Just Buy @=? oBuysell order let Just price' = oPrice order assertBool "Price is not valid" (12.34 ~== price') Just 12 @=? oQuantity order where validOrders = [r| 42 1 BOARD SEC1 CLIENT UNION active B 12.34 12 |] :: BS.ByteString testParseTradesStreaming :: TestTree testParseTradesStreaming = testCase "Parse ResponseTrades - valid XML (full)" $ do let [TransaqResponseTrades (ResponseTrades trades)] = parseTransaqResponses validTrades 1 @=? length trades let trade = head trades 1 @=? tSecId trade 12 @=? tTradeNo trade 42 @=? tOrderNo trade "BOARD" @=? tBoard trade "SEC1" @=? tSecCode trade "CLIENT" @=? tClient trade "UNION" @=? tUnion trade Buy @=? tBuysell trade -- TODO check time True @=? (123.4 ~== tValue trade) True @=? (0.5 ~== tComission trade) 10 @=? tQuantity trade True @=? (12.34 ~== tPrice trade) where validTrades = [r| 1 12 42 BOARD SEC1 CLIENT UNION B 123.40 0.5 10 12.34 |] :: BS.ByteString