{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE QuasiQuotes #-}
module Test.Transaq
(
unitTests
) where
import Control.Monad.State (execState)
import Data.AEq ((~==))
import qualified Data.ByteString as BS
import qualified Data.Text as T
import Test.Tasty
import Test.Tasty.HUnit (assertBool, testCase, (@=?))
import Text.RawString.QQ
import Text.XML.Light.Input (parseXMLDoc)
import Transaq
import Xeno.SAX (process)
unitTests :: TestTree
unitTests = testGroup "Parsing"
[
unitTestsXml
]
unitTestsXml :: TestTree
unitTestsXml = testGroup "Parsing via haskell-xml"
[
testParseResult
, testParseCandles
, testParseClient
, testParseClientWithoutForts
, testParseServerStatus
, testParseMarkets
, testParseCandleKinds
, testParseSecurities
, testParseSecInfo
, testParseQuotations
, testParseAllTrades
, testParseQuotes
, testParseOrders
, testParseTrades
]
testParseResult :: TestTree
testParseResult = testGroup "Parsing result"
[
testParseResultSuccess
, testParseResultSuccessWithTransactionId
, testParseResultFailure
]
where
resultSuccess = [r||] :: T.Text
testParseResultSuccess = testCase "Parse success result" $ do
(Just (ResponseSuccess Nothing)) @=? (parseXMLDoc resultSuccess >>= fromXml :: Maybe ResponseResult)
resultSuccessWithTransactionId = [r||] :: T.Text
testParseResultSuccessWithTransactionId = testCase "Parse success result with transaction ID" $ do
(Just (ResponseSuccess (Just 12))) @=? (parseXMLDoc resultSuccessWithTransactionId >>= fromXml :: Maybe ResponseResult)
resultFailure = [r||] :: T.Text
testParseResultFailure = testCase "Parse failure result" $ do
(Just (ResponseFailure "")) @=? (parseXMLDoc resultFailure >>= fromXml :: Maybe ResponseResult)
testParseCandles :: TestTree
testParseCandles = testCase "Parse ResponseCandles - valid XML" $ do
let (Just responseCandles) = (parseXMLDoc validCandles >>= fromXml :: Maybe ResponseCandles)
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|
|] :: T.Text
testParseClient :: TestTree
testParseClient = testCase "Parse ResponseClient - valid XML" $ do
let (Just (ResponseClient clientData)) = (parseXMLDoc validClient >>= fromXml :: Maybe ResponseClient)
"FOO" @=? cClientId clientData
"TYPE" @=? cType clientData
"CURRENCY" @=? cCurrency clientData
"MARKET" @=? cMarket clientData
"UNION" @=? cUnion clientData
Just "FORTS_ACC" @=? cForts clientData
where
validClient =
[r|
TYPE
CURRENCY
MARKET
UNION
FORTS_ACC
|] :: T.Text
testParseClientWithoutForts :: TestTree
testParseClientWithoutForts = testCase "Parse ResponseClient - valid XML (without FORTS account ID)" $ do
let (Just (ResponseClient clientData)) = (parseXMLDoc validClient >>= fromXml :: Maybe ResponseClient)
"FOO" @=? cClientId clientData
"TYPE" @=? cType clientData
"CURRENCY" @=? cCurrency clientData
"MARKET" @=? cMarket clientData
"UNION" @=? cUnion clientData
Nothing @=? cForts clientData
where
validClient =
[r|
TYPE
CURRENCY
MARKET
UNION
|] :: T.Text
testParseServerStatus :: TestTree
testParseServerStatus = testCase "Parse ResponseServerStatus - valid XML (full)" $ do
let (Just serverStatus) = (parseXMLDoc validServerStatus >>= fromXml :: Maybe ResponseServerStatus)
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| |] :: T.Text
testParseMarkets :: TestTree
testParseMarkets = testCase "Parse ResponseMarkets - valid XML" $ do
let (Just (ResponseMarkets markets)) = (parseXMLDoc validMarkets >>= fromXml :: Maybe ResponseMarkets)
2 @=? length markets
let [foo, bar] = markets
1 @=? marketId foo
"FOO" @=? marketName foo
2 @=? marketId bar
"BAR" @=? marketName bar
where
validMarkets =
[r|
FOO
BAR
|] :: T.Text
testParseCandleKinds :: TestTree
testParseCandleKinds = testCase "Parse ResponseCandleKinds - valid XML" $ do
let (Just (ResponseCandleKinds candleKinds)) = (parseXMLDoc validCandleKinds >>= fromXml :: Maybe ResponseCandleKinds)
2 @=? length candleKinds
let [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
|] :: T.Text
testParseSecurities :: TestTree
testParseSecurities = testCase "Parse ResponseSecurities - valid XML" $ do
let (Just (ResponseSecurities securities)) = (parseXMLDoc validSecurities >>= fromXml :: Maybe ResponseSecurities)
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
|] :: T.Text
testParseSecInfo :: TestTree
testParseSecInfo = testCase "Parse ResponseSecInfo - valid XML" $ do
let (Just sec) = (parseXMLDoc validSecInfo >>= fromXml :: Maybe ResponseSecInfo)
"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
|] :: T.Text
testParseQuotations :: TestTree
testParseQuotations = testCase "Parse ResponseQuotations - valid XML (full)" $ do
let (Just (ResponseQuotations quotations)) = (parseXMLDoc validQuotations >>= fromXml :: Maybe ResponseQuotations)
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
|] :: T.Text
testParseAllTrades :: TestTree
testParseAllTrades = testCase "Parse ResponseAllTrades - valid XML (full)" $ do
let (Just (ResponseAllTrades trades)) = (parseXMLDoc validAllTrades >>= fromXml :: Maybe ResponseAllTrades)
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
|] :: T.Text
testParseQuotes :: TestTree
testParseQuotes = testCase "Parse ResponseQuotes - valid XML (full)" $ do
let (Just (ResponseQuotes quotes)) = (parseXMLDoc validQuotes >>= fromXml :: Maybe ResponseQuotes)
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
|] :: T.Text
testParseOrders :: TestTree
testParseOrders = testCase "Parse ResponseOrders - valid XML (full)" $ do
let (Just (ResponseOrders orders)) = (parseXMLDoc validOrders >>= fromXml :: Maybe ResponseOrders)
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
|] :: T.Text
testParseTrades :: TestTree
testParseTrades = testCase "Parse ResponseTrades - valid XML (full)" $ do
let (Just (ResponseTrades trades)) = (parseXMLDoc validTrades >>= fromXml :: Maybe ResponseTrades)
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
|] :: T.Text