bitx-bitcoin 0.5.0.0 → 0.5.0.1
raw patch · 10 files changed
+166/−97 lines, 10 filesdep ~basedep ~http-conduitPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: base, http-conduit
API changes (from Hackage documentation)
Files
- README.md +34/−34
- bitx-bitcoin.cabal +16/−1
- src/Network/Bitcoin/BitX/Internal.hs +7/−7
- src/Network/Bitcoin/BitX/Types/Internal.hs +55/−28
- test/Example.hs +21/−0
- test/Network/Bitcoin/BitX/Spec/Common.hs +1/−1
- test/Network/Bitcoin/BitX/Spec/Specs/AesonRecordSpec.hs +19/−19
- test/Network/Bitcoin/BitX/Spec/Specs/JsonSpec.hs +6/−6
- test/Network/Bitcoin/BitX/Spec/Specs/PostSpec.hs +1/−1
- test/Network/Bitcoin/BitX/Spec/Specs/PrivateSpec.hs +6/−0
README.md view
@@ -1,40 +1,40 @@-[](https://travis-ci.org/tebello-thejane/bitx-haskell) -[](https://hackage.haskell.org/package/bitx-bitcoin) -[](http://creativecommons.org/publicdomain/zero/1.0/) - +[](https://travis-ci.org/tebello-thejane/bitx-haskell)+[](https://hackage.haskell.org/package/bitx-bitcoin)+[](http://creativecommons.org/publicdomain/zero/1.0/)+ (Hopefully useful) Haskell bindings to the [BitX](https://bitx.co/) bitcoin exchange's [API](https://bitx.co/api). As a minimal example, to get the current selling price (in South African Rand) of bitcoin on the-BitX exchange, do the following: - -```haskell -{-# LANGUAGE DataKinds #-} - -import Control.Lens ((^.)) -import Network.Bitcoin.BitX (BitXAPIResponse(..), getTicker, CcyPair(..)) -import qualified Network.Bitcoin.BitX as BitX -import Data.Text (unpack) -import Network.HTTP.Types.Status (Status(..)) -import Network.HTTP.Conduit (responseStatus) - -main :: IO () -main = do - bitXResponse <- getTicker XBTZAR - case bitXResponse of - ValidResponse tic -> print (tic ^. BitX.ask) - ErrorResponse err -> - error $ "BitX error received: \"" ++ unpack (err ^. BitX.error) ++ "\"" - ExceptionResponse ex -> - error $ "Exception was thrown: \"" ++ show ex ++ "\"" - UnparseableResponse resp -> - error $ "Bad HTTP response; HTTP status code was: \"" ++ (show . statusCode . responseStatus $ resp) ++ "\"" -``` - +BitX exchange, do the following:++```haskell+{-# LANGUAGE DataKinds #-}++import Control.Lens ((^.))+import Network.Bitcoin.BitX (BitXAPIResponse(..), getTicker, CcyPair(..))+import qualified Network.Bitcoin.BitX as BitX+import Data.Text (unpack)+import Network.HTTP.Types.Status (Status(..))+import Network.HTTP.Conduit (responseStatus)++main :: IO ()+main = do+ bitXResponse <- getTicker XBTZAR+ case bitXResponse of+ ValidResponse tic -> print (tic ^. BitX.ask)+ ErrorResponse err ->+ error $ "BitX error received: \"" ++ unpack (err ^. BitX.error) ++ "\""+ ExceptionResponse ex ->+ error $ "Exception was thrown: \"" ++ show ex ++ "\""+ UnparseableResponse resp ->+ error $ "Bad HTTP response; HTTP status code was: \"" ++ (show . statusCode . responseStatus $ resp) ++ "\""+```+ Note that the code snippet above depends on [http-types](https://hackage.haskell.org/package/http-types),-[http-conduit](https://hackage.haskell.org/package/http-conduit), [lens](https://hackage.haskell.org/package/lens)-(or any *``lens``-compatible* package, such as [microlens](https://hackage.haskell.org/package/microlens)),-and finally **bitx-bitcoin**. - +[text](https://hackage.haskell.org/package/text), [http-conduit](https://hackage.haskell.org/package/http-conduit),+[lens](https://hackage.haskell.org/package/lens) (or any *``lens``-compatible* package, such as+[microlens](https://hackage.haskell.org/package/microlens)), and finally **bitx-bitcoin**.+ This library is known to work on Windows, but if you wish to use it then you will have to do a bit more work due to the ``Network`` library not building on Windows out of the box. See-[this blog post by Neil Mitchell](http://neilmitchell.blogspot.com/2010/12/installing-haskell-network-library-on.html). +[this blog post by Neil Mitchell](http://neilmitchell.blogspot.com/2010/12/installing-haskell-network-library-on.html).
bitx-bitcoin.cabal view
@@ -1,5 +1,5 @@ name: bitx-bitcoin-version: 0.5.0.0+version: 0.5.0.1 synopsis: A Haskell library for working with the BitX bitcoin exchange. description:@@ -124,5 +124,20 @@ main-is: Doctests.hs build-depends: base, doctest >= 0.8+ hs-source-dirs: test+ default-language: Haskell2010++--------------------------------------------------------------------------------++test-suite example+ type: exitcode-stdio-1.0+ ghc-options: -threaded+ main-is: Example.hs+ build-depends: base,+ microlens,+ bitx-bitcoin,+ http-types,+ http-conduit,+ text hs-source-dirs: test default-language: Haskell2010
src/Network/Bitcoin/BitX/Internal.hs view
@@ -50,14 +50,14 @@ userID = Txt.encodeUtf8 (auth ^. Types.id) userSecret = Txt.encodeUtf8 (auth ^. Types.secret) -simpleBitXGetAuth_ :: BitXAesRecordConvert recd aes => BitXAuth -> String -> IO (BitXAPIResponse recd)+simpleBitXGetAuth_ :: BitXAesRecordConvert recd => BitXAuth -> String -> IO (BitXAPIResponse recd) simpleBitXGetAuth_ auth verb = withSocketsDo $ rateLimit (authConnect auth . fromJust . NetCon.parseUrl $ (bitXAPIRoot ++ verb)) consumeResponseIO -simpleBitXPOSTAuth_ :: (BitXAesRecordConvert recd aes, POSTEncodeable inprec) => BitXAuth -> inprec+simpleBitXPOSTAuth_ :: (BitXAesRecordConvert recd, POSTEncodeable inprec) => BitXAuth -> inprec -> String -> IO (BitXAPIResponse recd) simpleBitXPOSTAuth_ auth encrec verb = withSocketsDo $ rateLimit@@ -66,14 +66,14 @@ . fromJust . NetCon.parseUrl $ (bitXAPIRoot ++ verb)) consumeResponseIO -simpleBitXMETHAuth_ :: BitXAesRecordConvert recd aes => BitXAuth -> BS.ByteString+simpleBitXMETHAuth_ :: BitXAesRecordConvert recd => BitXAuth -> BS.ByteString -> String -> IO (BitXAPIResponse recd) simpleBitXMETHAuth_ auth meth verb = withSocketsDo $ rateLimit (authConnect auth (fromJust (NetCon.parseUrl (bitXAPIRoot ++ verb))) { method = meth }) consumeResponseIO -simpleBitXGet_ :: BitXAesRecordConvert recd aes => String -> IO (BitXAPIResponse recd)+simpleBitXGet_ :: BitXAesRecordConvert recd => String -> IO (BitXAPIResponse recd) simpleBitXGet_ verb = withSocketsDo $ do manager <- globalManager rateLimit@@ -81,7 +81,7 @@ consumeResponseIO rateLimit :: IO (Either NetCon.HttpException c) -> (Either NetCon.HttpException c -> IO d) -> IO d-rateLimit act1 act2 = go 500000+rateLimit act1 act2 = go (500 * 000) where go del = do resp <- act1@@ -95,14 +95,14 @@ maxLimit = 5 * 1000 * 1000 -- 5 seconds probably means something else is wrong... incDelay = round . (* (1.5 :: Double)) . fromIntegral -consumeResponseIO :: BitXAesRecordConvert recd aes => Either NetCon.HttpException (NetCon.Response BL.ByteString)+consumeResponseIO :: BitXAesRecordConvert recd => Either NetCon.HttpException (NetCon.Response BL.ByteString) -> IO (BitXAPIResponse recd) consumeResponseIO resp = return $ case resp of Left ex -> ExceptionResponse ex Right k -> bitXErrorOrPayload k -bitXErrorOrPayload :: BitXAesRecordConvert recd aes => Response BL.ByteString -> BitXAPIResponse recd+bitXErrorOrPayload :: BitXAesRecordConvert recd => Response BL.ByteString -> BitXAPIResponse recd bitXErrorOrPayload resp = fromJust $ ErrorResponse . aesToRec <$> Aeson.decode body -- is it a BitX error? <|> ValidResponse . aesToRec <$> Aeson.decode body
src/Network/Bitcoin/BitX/Types/Internal.hs view
@@ -1,5 +1,5 @@-{-# LANGUAGE QuasiQuotes, OverloadedStrings, TemplateHaskell, MultiParamTypeClasses,- FunctionalDependencies, FlexibleInstances, DataKinds, CPP, RecordWildCards #-}+{-# LANGUAGE QuasiQuotes, OverloadedStrings, TemplateHaskell, TypeFamilies, FlexibleContexts,+ FlexibleInstances, DataKinds, CPP, RecordWildCards #-} module Network.Bitcoin.BitX.Types.Internal (@@ -41,9 +41,13 @@ . ( / 1000) . (fromIntegral :: Integer -> Scientific) -class (FromJSON aes) => BitXAesRecordConvert recd aes | recd -> aes where- aesToRec :: aes -> recd+--class (FromJSON aes) => BitXAesRecordConvert recd aes | recd -> aes where+-- aesToRec :: aes -> recd +class FromJSON (Aes recd) => BitXAesRecordConvert recd where+ type Aes recd+ aesToRec :: Aes recd -> recd+ class POSTEncodeable recd where postEncode :: recd -> [(ByteString, ByteString)] @@ -65,7 +69,7 @@ parseJSON _ = mempty instance FromJSON QuotedInt where- parseJSON (String x) = return . QuotedInt . read . Txt.unpack $ x+ parseJSON (String x) = return . QuotedInt . (truncate :: Scientific -> Int) . read . Txt.unpack $ x parseJSON (Number x) = return . QuotedInt . (truncate :: Scientific -> Int) . read . show $ x parseJSON _ = mempty @@ -143,7 +147,8 @@ $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''Ticker_) -instance BitXAesRecordConvert Types.Ticker Ticker_ where+instance BitXAesRecordConvert Types.Ticker where+ type Aes Types.Ticker = Ticker_ aesToRec (Ticker_ {..}) = Types.Ticker {tickerTimestamp = tsmsToUTCTime ticker'timestamp, tickerBid = qiToInt ticker'bid,@@ -161,7 +166,8 @@ $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''Tickers_) -instance BitXAesRecordConvert [Types.Ticker] Tickers_ where+instance BitXAesRecordConvert [Types.Ticker] where+ type Aes [Types.Ticker] = Tickers_ aesToRec (Tickers_ {..}) = map aesToRec tickers'tickers @@ -174,7 +180,8 @@ $(AesTH.deriveJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''BitXError_) -instance BitXAesRecordConvert Types.BitXError BitXError_ where+instance BitXAesRecordConvert Types.BitXError where+ type Aes Types.BitXError = BitXError_ aesToRec (BitXError_ {..}) = Types.BitXError { bitXErrorError = bitXError'error, bitXErrorErrorCode = bitXError'error_code} @@ -187,7 +194,8 @@ $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''Order_) -instance BitXAesRecordConvert Types.Order Order_ where+instance BitXAesRecordConvert Types.Order where+ type Aes Types.Order = Order_ aesToRec (Order_ {..}) = Types.Order {orderVolume = qsToScientific order'volume, orderPrice = qiToInt order'price}@@ -206,7 +214,8 @@ $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''Orderbook_) -instance BitXAesRecordConvert Types.Orderbook Orderbook_ where+instance BitXAesRecordConvert Types.Orderbook where+ type Aes Types.Orderbook = Orderbook_ aesToRec (Orderbook_ {..}) = Types.Orderbook {orderbookTimestamp = tsmsToUTCTime orderbook'timestamp, orderbookBids = map aesToRec orderbook'bids,@@ -223,7 +232,8 @@ $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''Trade_) -instance BitXAesRecordConvert Types.Trade Trade_ where+instance BitXAesRecordConvert Types.Trade where+ type Aes Types.Trade = Trade_ aesToRec (Trade_ {..}) = Types.Trade { tradeTimestamp = tsmsToUTCTime trade'timestamp, tradeVolume = qsToScientific trade'volume,@@ -238,7 +248,8 @@ $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''PublicTrades_) -instance BitXAesRecordConvert [Types.Trade] PublicTrades_ where+instance BitXAesRecordConvert [Types.Trade] where+ type Aes [Types.Trade] = PublicTrades_ aesToRec (PublicTrades_ {..}) = map aesToRec publicTrades'trades @@ -262,7 +273,8 @@ $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''PrivateOrder_) -instance BitXAesRecordConvert Types.PrivateOrder PrivateOrder_ where+instance BitXAesRecordConvert Types.PrivateOrder where+ type Aes Types.PrivateOrder = PrivateOrder_ aesToRec (PrivateOrder_ {..}) = Types.PrivateOrder {privateOrderBase = qsToScientific privateOrder'base, privateOrderCounter = qsToScientific privateOrder'counter,@@ -286,7 +298,8 @@ $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''PrivateOrders_) -instance BitXAesRecordConvert [Types.PrivateOrder] PrivateOrders_ where+instance BitXAesRecordConvert [Types.PrivateOrder] where+ type Aes [Types.PrivateOrder] = PrivateOrders_ aesToRec (PrivateOrders_ {..}) = map aesToRec privateOrders'orders @@ -308,7 +321,8 @@ $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''OrderIDRec_) -instance BitXAesRecordConvert Types.OrderID OrderIDRec_ where+instance BitXAesRecordConvert Types.OrderID where+ type Aes Types.OrderID = OrderIDRec_ aesToRec (OrderIDRec_ {..}) = orderIDResponse'order_id @@ -325,7 +339,8 @@ $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''RequestSuccess_) -instance BitXAesRecordConvert Types.RequestSuccess RequestSuccess_ where+instance BitXAesRecordConvert Types.RequestSuccess where+ type Aes Types.RequestSuccess = RequestSuccess_ aesToRec (RequestSuccess_ {..}) = requestSuccess'success @@ -350,7 +365,8 @@ $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''PrivateOrderWithTrades_) -instance BitXAesRecordConvert Types.PrivateOrderWithTrades PrivateOrderWithTrades_ where+instance BitXAesRecordConvert Types.PrivateOrderWithTrades where+ type Aes Types.PrivateOrderWithTrades = PrivateOrderWithTrades_ aesToRec (PrivateOrderWithTrades_ {..}) = Types.PrivateOrderWithTrades {privateOrderWithTradesBase = qsToScientific privateOrderWithTrades'base, privateOrderWithTradesCounter = qsToScientific privateOrderWithTrades'counter,@@ -379,7 +395,8 @@ $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''Balance_) -instance BitXAesRecordConvert Types.Balance Balance_ where+instance BitXAesRecordConvert Types.Balance where+ type Aes Types.Balance = Balance_ aesToRec (Balance_ {..}) = Types.Balance {balanceId = balance'account_id, balanceAsset = balance'asset,@@ -396,7 +413,8 @@ $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''Balances_) -instance BitXAesRecordConvert [Types.Balance] Balances_ where+instance BitXAesRecordConvert [Types.Balance] where+ type Aes [Types.Balance] = Balances_ aesToRec (Balances_ {..}) = map aesToRec balances'balance @@ -412,7 +430,8 @@ $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''FundingAddress_) -instance BitXAesRecordConvert Types.FundingAddress FundingAddress_ where+instance BitXAesRecordConvert Types.FundingAddress where+ type Aes Types.FundingAddress = FundingAddress_ aesToRec (FundingAddress_ {..}) = Types.FundingAddress {fundingAddressAsset = fundingAdress'asset, fundingAddressAddress = fundingAdress'address,@@ -435,7 +454,8 @@ $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''WithdrawalRequest_) -instance BitXAesRecordConvert Types.WithdrawalRequest WithdrawalRequest_ where+instance BitXAesRecordConvert Types.WithdrawalRequest where+ type Aes Types.WithdrawalRequest = WithdrawalRequest_ aesToRec (WithdrawalRequest_ {..}) = Types.WithdrawalRequest {withdrawalRequestStatus = requestStatusParse withdrawalRequest'status, withdrawalRequestId = withdrawalRequest'id}@@ -449,7 +469,8 @@ $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''WithdrawalRequests_) -instance BitXAesRecordConvert [Types.WithdrawalRequest] WithdrawalRequests_ where+instance BitXAesRecordConvert [Types.WithdrawalRequest] where+ type Aes [Types.WithdrawalRequest] = WithdrawalRequests_ aesToRec (WithdrawalRequests_ {..}) = map aesToRec withdrawalRequests'withdrawals @@ -498,7 +519,8 @@ $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''OrderQuote_) -instance BitXAesRecordConvert Types.OrderQuote OrderQuote_ where+instance BitXAesRecordConvert Types.OrderQuote where+ type Aes Types.OrderQuote = OrderQuote_ aesToRec (OrderQuote_ {..}) = Types.OrderQuote {orderQuoteId = orderQuote'id, orderQuoteQuoteType = orderQuote'type,@@ -520,7 +542,8 @@ $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''BitXAuth_) -instance BitXAesRecordConvert Types.BitXAuth BitXAuth_ where+instance BitXAesRecordConvert Types.BitXAuth where+ type Aes Types.BitXAuth = BitXAuth_ aesToRec (BitXAuth_ {..}) = Types.BitXAuth {bitXAuthId = bitXAuth'api_key_id, bitXAuthSecret = bitXAuth'api_key_secret}@@ -541,7 +564,8 @@ $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''Transaction_) -instance BitXAesRecordConvert Types.Transaction Transaction_ where+instance BitXAesRecordConvert Types.Transaction where+ type Aes Types.Transaction = Transaction_ aesToRec (Transaction_ {..}) = Types.Transaction {transactionRowIndex = transaction'row_index, transactionTimestamp = tsmsToUTCTime transaction'timestamp,@@ -561,7 +585,8 @@ $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''Transactions_) -instance BitXAesRecordConvert [Types.Transaction] Transactions_ where+instance BitXAesRecordConvert [Types.Transaction] where+ type Aes [Types.Transaction] = Transactions_ aesToRec (Transactions_ {..}) = map aesToRec transactions'transactions @@ -573,7 +598,8 @@ $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''PendingTransactions_) -instance BitXAesRecordConvert PendingTransactions__ PendingTransactions_ where+instance BitXAesRecordConvert PendingTransactions__ where+ type Aes PendingTransactions__ = PendingTransactions_ aesToRec (PendingTransactions_ {..}) = PendingTransactions__ {pendingTransactions__transactions = map aesToRec transactions'pending} @@ -594,7 +620,8 @@ $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''Account_) -instance BitXAesRecordConvert Types.Account Account_ where+instance BitXAesRecordConvert Types.Account where+ type Aes Types.Account = Account_ aesToRec (Account_ {..}) = Types.Account {accountId = account'id, accountName = account'name,
+ test/Example.hs view
@@ -0,0 +1,21 @@+{-# LANGUAGE DataKinds #-}++import Lens.Micro ((^.))+import Network.Bitcoin.BitX (BitXAPIResponse(..), getTicker, CcyPair(..))+import qualified Network.Bitcoin.BitX as BitX+import Data.Text (unpack)+import Network.HTTP.Types.Status (Status(..))+import Network.HTTP.Conduit (responseStatus)++main :: IO ()+main = do+ bitXResponse <- getTicker XBTZAR+ case bitXResponse of+ ValidResponse tic -> putStrLn ("1 bitcoin will set you back ZAR" ++ show (tic ^. BitX.ask) ++ ".00.")+ ErrorResponse err ->+ error $ "BitX error received: \"" ++ unpack (err ^. BitX.error) ++ "\""+ ExceptionResponse ex ->+ error $ "Exception was thrown: \"" ++ show ex ++ "\""+ UnparseableResponse resp ->+ error $ "Bad HTTP response; HTTP status code was: \"" ++ (show . statusCode . responseStatus $ resp) ++ "\""+
test/Network/Bitcoin/BitX/Spec/Common.hs view
@@ -8,5 +8,5 @@ import Network.Bitcoin.BitX (BitXAesRecordConvert(..)) import Data.ByteString.Lazy (ByteString) -recordAesCheck :: (BitXAesRecordConvert recd aes, Show recd, Eq recd) => ByteString -> recd -> Expectation+recordAesCheck :: (BitXAesRecordConvert recd, Show recd, Eq recd) => ByteString -> recd -> Expectation recordAesCheck aesTxt recd = fmap aesToRec (decode aesTxt) `shouldBe` Just recd
test/Network/Bitcoin/BitX/Spec/Specs/AesonRecordSpec.hs view
@@ -19,8 +19,8 @@ BitXError {bitXErrorError = "oops", bitXErrorErrorCode = "ABadError"} it "Ticker is parsed properly" $ recordAesCheck- "{\"timestamp\":1431811395699,\"bid\":\"3083\",\"ask\":\"3115\",\- \ \"last_trade\":\"3116\",\"rolling_24_hour_volume\":\"19.776608\",\"pair\":\"XBTZAR\"}"+ "{\"timestamp\":1431811395699,\"bid\":\"3083.00\",\"ask\":\"3115.00\",\+ \ \"last_trade\":\"3116.00\",\"rolling_24_hour_volume\":\"19.776608\",\"pair\":\"XBTZAR\"}" Ticker { tickerTimestamp = posixSecondsToUTCTime 1431811395.699, tickerBid = 3083,@@ -30,18 +30,18 @@ tickerPair = XBTZAR} it "Balance is parsed properly" $ recordAesCheck- "{\"account_id\":\"314159\",\"asset\":\"ZAR\",\"balance\":\"2159.15\",\"reserved\":\"320\",\- \ \"unconfirmed\":\"175\"}"+ "{\"account_id\":\"314159\",\"asset\":\"ZAR\",\"balance\":\"2159.15\",\"reserved\":\"320.43\",\+ \ \"unconfirmed\":\"175.34\"}" Balance {balanceId = "314159", balanceAsset = ZAR, balanceBalance = 2159.15,- balanceReserved = 320,- balanceUnconfirmed = 175}+ balanceReserved = 320.43,+ balanceUnconfirmed = 175.34} it "Order is parsed properly" $ recordAesCheck- "{\"volume\":\"314159\",\"price\":4321}"- Order {orderVolume = 314159, orderPrice = 4321}+ "{\"volume\":\"314159.26\",\"price\":4321.00}"+ Order {orderVolume = 314159.26, orderPrice = 4321} it "WithdrawalRequest is parsed properly" $ recordAesCheck "{\"status\":\"PENDING\",\"id\":\"271828\"}"@@ -50,21 +50,21 @@ withdrawalRequestId = "271828" } it "Tickers is parsed properly" $ recordAesCheck- "{\"tickers\":[{\"timestamp\":1431811395699,\"bid\":\"3083\",\"ask\":\"3115\",\- \ \"last_trade\":\"3116\",\"rolling_24_hour_volume\":\"19.776608\",\+ "{\"tickers\":[{\"timestamp\":1431811395699,\"bid\":\"3083.00\",\"ask\":\"3115.00\",\+ \ \"last_trade\":\"3116.00\",\"rolling_24_hour_volume\":\"19.776608\",\ \ \"pair\":\"XBTZAR\"}]}" [tickerInner] it "Orderbook is parsed properly" $ recordAesCheck- "{\"timestamp\":1431811395699,\"bids\":[{\"volume\":\"654.98\",\"price\":\"3789\"}],\- \ \"asks\":[{\"volume\":\"654.98\",\"price\":\"3789\"}]}"+ "{\"timestamp\":1431811395699,\"bids\":[{\"volume\":\"654.98\",\"price\":\"3789.00\"}],\+ \ \"asks\":[{\"volume\":\"654.98\",\"price\":\"3789.00\"}]}" Orderbook {orderbookTimestamp = posixSecondsToUTCTime 1431811395.699, orderbookBids = [orderInner], orderbookAsks = [orderInner]} it "Trade is parsed properly" $ recordAesCheck- "{\"timestamp\":1431811395699,\"volume\":\"6754.09\",\"price\":\"5327\"}"+ "{\"timestamp\":1431811395699,\"volume\":\"6754.09\",\"price\":\"5327.00\"}" Trade {tradeTimestamp = posixSecondsToUTCTime 1431811395.699, tradeVolume = 6754.09,@@ -72,13 +72,13 @@ it "PublicTrades is parsed properly" $ recordAesCheck "{\"trades\":[{\"timestamp\":1431811395699,\"volume\":\"6754.09\",\- \ \"price\":\"5327\"}],\"currency\":\"ZAR\"}"+ \ \"price\":\"5327.00\"}],\"currency\":\"ZAR\"}" [tradeInner] it "PrivateOrder is parsed properly" $ recordAesCheck "{\"base\":\"568.7\", \"counter\":3764.2,\"creation_timestamp\":478873467, \ \ \"expiration_timestamp\":8768834222, \"fee_base\":\"3687.3\", \"fee_counter\":12.9,\- \ \"limit_price\":765,\"limit_volume\":55.2,\"order_id\":\"83YG\",\"pair\":\"NADXBT\",\+ \ \"limit_price\":765.00,\"limit_volume\":55.2,\"order_id\":\"83YG\",\"pair\":\"NADXBT\",\ \ \"state\":\"COMPLETE\",\"type\":\"BID\"}" PrivateOrder {privateOrderBase = 568.7,@@ -97,7 +97,7 @@ recordAesCheck "{\"orders\":[{\"base\":\"568.7\", \"counter\":3764.2,\"creation_timestamp\":478873467, \ \ \"expiration_timestamp\":8768834222, \"fee_base\":\"3687.3\", \"fee_counter\":12.9,\- \ \"limit_price\":765,\"limit_volume\":55.2,\"order_id\":\"83YG\",\"pair\":\"NADXBT\",\+ \ \"limit_price\":765.00,\"limit_volume\":55.2,\"order_id\":\"83YG\",\"pair\":\"NADXBT\",\ \ \"state\":\"COMPLETE\",\"type\":\"BID\"}]}" [privateOrderInner] it "OrderID is parsed properly" $@@ -106,7 +106,7 @@ ("57983" :: OrderID) it "PublicTrades is parsed properly" $ recordAesCheck- "{\"trades\":[{\"timestamp\":1431811395699,\"volume\":\"6754.09\",\"price\":\"5327\"}], \+ "{\"trades\":[{\"timestamp\":1431811395699,\"volume\":\"6754.09\",\"price\":\"5327.00\"}], \ \ \"currency\":\"ZAR\"}" [tradeInner] it "RequestSuccess is parsed properly" $@@ -117,9 +117,9 @@ recordAesCheck "{\"base\":\"568.7\", \"counter\":3764.2,\"creation_timestamp\":478873467, \ \ \"expiration_timestamp\":8768834222, \"fee_base\":\"3687.3\", \"fee_counter\":12.9,\- \ \"limit_price\":765,\"limit_volume\":55.2,\"order_id\":\"83YG\",\"pair\":\"NADXBT\",\+ \ \"limit_price\":765.00,\"limit_volume\":55.2,\"order_id\":\"83YG\",\"pair\":\"NADXBT\",\ \ \"state\":\"COMPLETE\",\"type\":\"BID\", \"trades\":[{\"timestamp\":1431811395699, \- \ \"volume\":\"6754.09\",\"price\":\"5327\"}]}"+ \ \"volume\":\"6754.09\",\"price\":\"5327.00\"}]}" PrivateOrderWithTrades {privateOrderWithTradesBase = 568.7, privateOrderWithTradesCounter = 3764.2,
test/Network/Bitcoin/BitX/Spec/Specs/JsonSpec.hs view
@@ -31,17 +31,17 @@ (Order 314159 4321) it "QuotedInt is parsed when quoted" $ recordAesCheck- "{\"volume\":0,\"price\":\"4321\"}"+ "{\"volume\":0,\"price\":\"4321.00\"}" (Order 0 4321) it "QuotedInt is parsed when not quoted" $ recordAesCheck- "{\"volume\":0,\"price\":4321}"+ "{\"volume\":0,\"price\":4321.00}" (Order 0 4321) it "OrderType BUY is parsed properly" $ recordAesCheck "{\"base\":\"568.7\", \"counter\":3764.2,\"creation_timestamp\":478873467, \ \\"expiration_timestamp\":8768834222, \"fee_base\":\"3687.3\", \"fee_counter\":12.9,\- \\"limit_price\":765,\"limit_volume\":55.2,\"order_id\":\"83YG\",\"pair\":\"NADXBT\",\+ \\"limit_price\":765.00,\"limit_volume\":55.2,\"order_id\":\"83YG\",\"pair\":\"NADXBT\",\ \\"state\":\"COMPLETE\",\"type\":\"BUY\"}" PrivateOrder {privateOrderBase = 568.7,@@ -60,7 +60,7 @@ recordAesCheck "{\"base\":\"568.7\", \"counter\":3764.2,\"creation_timestamp\":478873467, \ \\"expiration_timestamp\":8768834222, \"fee_base\":\"3687.3\", \"fee_counter\":12.9,\- \\"limit_price\":765,\"limit_volume\":55.2,\"order_id\":\"83YG\",\"pair\":\"NADXBT\",\+ \\"limit_price\":765.00,\"limit_volume\":55.2,\"order_id\":\"83YG\",\"pair\":\"NADXBT\",\ \\"state\":\"COMPLETE\",\"type\":\"BID\"}" PrivateOrder {privateOrderBase = 568.7,@@ -79,7 +79,7 @@ recordAesCheck "{\"base\":\"568.7\", \"counter\":3764.2,\"creation_timestamp\":478873467, \ \\"expiration_timestamp\":8768834222, \"fee_base\":\"3687.3\", \"fee_counter\":12.9,\- \\"limit_price\":765,\"limit_volume\":55.2,\"order_id\":\"83YG\",\"pair\":\"NADXBT\",\+ \\"limit_price\":765.00,\"limit_volume\":55.2,\"order_id\":\"83YG\",\"pair\":\"NADXBT\",\ \\"state\":\"COMPLETE\",\"type\":\"ASK\"}" PrivateOrder {privateOrderBase = 568.7,@@ -98,7 +98,7 @@ recordAesCheck "{\"base\":\"568.7\", \"counter\":3764.2,\"creation_timestamp\":478873467, \ \\"expiration_timestamp\":8768834222, \"fee_base\":\"3687.3\", \"fee_counter\":12.9,\- \\"limit_price\":765,\"limit_volume\":55.2,\"order_id\":\"83YG\",\"pair\":\"NADXBT\",\+ \\"limit_price\":765.00,\"limit_volume\":55.2,\"order_id\":\"83YG\",\"pair\":\"NADXBT\",\ \\"state\":\"COMPLETE\",\"type\":\"SELL\"}" PrivateOrder {privateOrderBase = 568.7,
test/Network/Bitcoin/BitX/Spec/Specs/PostSpec.hs view
@@ -17,7 +17,7 @@ {orderRequestPair = XBTZAR, orderRequestOrderType = BID, orderRequestVolume = 83.02,- orderRequestPrice = 15 }+ orderRequestPrice = 15} `shouldBe` [("pair", "XBTZAR"), ("type", "BID"),
test/Network/Bitcoin/BitX/Spec/Specs/PrivateSpec.hs view
@@ -19,6 +19,12 @@ --import Text.Show.Pretty (parseValue, valToStr) import Safe +-- To use this spec, create a file called "PRIVATE_API_KEY" with the private key and secret, respectively.+-- placed on their own lines.+--+-- To ensure that nothing funny happens, you can create an Api key without the Perm_W_Withdrawals+-- permission.+ spec :: Spec spec = describe "Private functionality test" $ do mauth <- runIO privateAuth