{-# 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