bitx-bitcoin 0.11.0.1 → 0.12.0.0
raw patch · 13 files changed
+107/−128 lines, 13 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- Network.Bitcoin.BitX.Types: IDRXBT :: CcyPair
- Network.Bitcoin.BitX.Types: KES :: Asset
- Network.Bitcoin.BitX.Types: KESXBT :: CcyPair
- Network.Bitcoin.BitX.Types: MYRXBT :: CcyPair
- Network.Bitcoin.BitX.Types: NAD :: Asset
- Network.Bitcoin.BitX.Types: NADXBT :: CcyPair
- Network.Bitcoin.BitX.Types: NGN :: Asset
- Network.Bitcoin.BitX.Types: NGNXBT :: CcyPair
- Network.Bitcoin.BitX.Types: SGD :: Asset
- Network.Bitcoin.BitX.Types: SGDXBT :: CcyPair
- Network.Bitcoin.BitX.Types: XBTKES :: CcyPair
- Network.Bitcoin.BitX.Types: XBTNAD :: CcyPair
- Network.Bitcoin.BitX.Types: XBTSGD :: CcyPair
- Network.Bitcoin.BitX.Types: ZARXBT :: CcyPair
- Network.Bitcoin.BitX.Types: instance Network.Bitcoin.BitX.Types.HasAsk Network.Bitcoin.BitX.Types.Ticker (GHC.Base.Maybe GHC.Types.Int)
- Network.Bitcoin.BitX.Types: instance Network.Bitcoin.BitX.Types.HasBid Network.Bitcoin.BitX.Types.Ticker (GHC.Base.Maybe GHC.Types.Int)
- Network.Bitcoin.BitX.Types: instance Network.Bitcoin.BitX.Types.HasCompletedTimestamp Network.Bitcoin.BitX.Types.PrivateOrder Data.Time.Clock.UTC.UTCTime
- Network.Bitcoin.BitX.Types: instance Network.Bitcoin.BitX.Types.HasCreatedAt Network.Bitcoin.BitX.Types.OrderQuote Data.Time.Clock.UTC.UTCTime
- Network.Bitcoin.BitX.Types: instance Network.Bitcoin.BitX.Types.HasCreationTimestamp Network.Bitcoin.BitX.Types.PrivateOrder Data.Time.Clock.UTC.UTCTime
- Network.Bitcoin.BitX.Types: instance Network.Bitcoin.BitX.Types.HasExpirationTimestamp Network.Bitcoin.BitX.Types.PrivateOrder Data.Time.Clock.UTC.UTCTime
- Network.Bitcoin.BitX.Types: instance Network.Bitcoin.BitX.Types.HasExpiresAt Network.Bitcoin.BitX.Types.OrderQuote Data.Time.Clock.UTC.UTCTime
- Network.Bitcoin.BitX.Types: instance Network.Bitcoin.BitX.Types.HasLastTrade Network.Bitcoin.BitX.Types.Ticker (GHC.Base.Maybe GHC.Types.Int)
- Network.Bitcoin.BitX.Types: instance Network.Bitcoin.BitX.Types.HasLimitPrice Network.Bitcoin.BitX.Types.PrivateOrder GHC.Types.Int
- Network.Bitcoin.BitX.Types: instance Network.Bitcoin.BitX.Types.HasPrice Network.Bitcoin.BitX.Types.Order GHC.Types.Int
- Network.Bitcoin.BitX.Types: instance Network.Bitcoin.BitX.Types.HasPrice Network.Bitcoin.BitX.Types.OrderRequest GHC.Types.Int
- Network.Bitcoin.BitX.Types: instance Network.Bitcoin.BitX.Types.HasPrice Network.Bitcoin.BitX.Types.PrivateTrade GHC.Types.Int
- Network.Bitcoin.BitX.Types: instance Network.Bitcoin.BitX.Types.HasPrice Network.Bitcoin.BitX.Types.Trade GHC.Types.Int
- Network.Bitcoin.BitX.Types: instance Network.Bitcoin.BitX.Types.HasTimestamp Network.Bitcoin.BitX.Types.Orderbook Data.Time.Clock.UTC.UTCTime
- Network.Bitcoin.BitX.Types: instance Network.Bitcoin.BitX.Types.HasTimestamp Network.Bitcoin.BitX.Types.PrivateTrade Data.Time.Clock.UTC.UTCTime
- Network.Bitcoin.BitX.Types: instance Network.Bitcoin.BitX.Types.HasTimestamp Network.Bitcoin.BitX.Types.Ticker Data.Time.Clock.UTC.UTCTime
- Network.Bitcoin.BitX.Types: instance Network.Bitcoin.BitX.Types.HasTimestamp Network.Bitcoin.BitX.Types.Trade Data.Time.Clock.UTC.UTCTime
- Network.Bitcoin.BitX.Types: instance Network.Bitcoin.BitX.Types.HasTimestamp Network.Bitcoin.BitX.Types.Transaction Data.Time.Clock.UTC.UTCTime
+ Network.Bitcoin.BitX.Types: BCH :: Asset
+ Network.Bitcoin.BitX.Types: ETH :: Asset
+ Network.Bitcoin.BitX.Types: ETHXBT :: CcyPair
+ Network.Bitcoin.BitX.Types: instance Network.Bitcoin.BitX.Types.HasAsk Network.Bitcoin.BitX.Types.Ticker (GHC.Base.Maybe Data.Scientific.Scientific)
+ Network.Bitcoin.BitX.Types: instance Network.Bitcoin.BitX.Types.HasBid Network.Bitcoin.BitX.Types.Ticker (GHC.Base.Maybe Data.Scientific.Scientific)
+ Network.Bitcoin.BitX.Types: instance Network.Bitcoin.BitX.Types.HasCompletedTimestamp Network.Bitcoin.BitX.Types.PrivateOrder Data.Time.Clock.Internal.UTCTime.UTCTime
+ Network.Bitcoin.BitX.Types: instance Network.Bitcoin.BitX.Types.HasCreatedAt Network.Bitcoin.BitX.Types.OrderQuote Data.Time.Clock.Internal.UTCTime.UTCTime
+ Network.Bitcoin.BitX.Types: instance Network.Bitcoin.BitX.Types.HasCreationTimestamp Network.Bitcoin.BitX.Types.PrivateOrder Data.Time.Clock.Internal.UTCTime.UTCTime
+ Network.Bitcoin.BitX.Types: instance Network.Bitcoin.BitX.Types.HasExpirationTimestamp Network.Bitcoin.BitX.Types.PrivateOrder Data.Time.Clock.Internal.UTCTime.UTCTime
+ Network.Bitcoin.BitX.Types: instance Network.Bitcoin.BitX.Types.HasExpiresAt Network.Bitcoin.BitX.Types.OrderQuote Data.Time.Clock.Internal.UTCTime.UTCTime
+ Network.Bitcoin.BitX.Types: instance Network.Bitcoin.BitX.Types.HasLastTrade Network.Bitcoin.BitX.Types.Ticker (GHC.Base.Maybe Data.Scientific.Scientific)
+ Network.Bitcoin.BitX.Types: instance Network.Bitcoin.BitX.Types.HasLimitPrice Network.Bitcoin.BitX.Types.PrivateOrder Data.Scientific.Scientific
+ Network.Bitcoin.BitX.Types: instance Network.Bitcoin.BitX.Types.HasPrice Network.Bitcoin.BitX.Types.Order Data.Scientific.Scientific
+ Network.Bitcoin.BitX.Types: instance Network.Bitcoin.BitX.Types.HasPrice Network.Bitcoin.BitX.Types.OrderRequest Data.Scientific.Scientific
+ Network.Bitcoin.BitX.Types: instance Network.Bitcoin.BitX.Types.HasPrice Network.Bitcoin.BitX.Types.PrivateTrade Data.Scientific.Scientific
+ Network.Bitcoin.BitX.Types: instance Network.Bitcoin.BitX.Types.HasPrice Network.Bitcoin.BitX.Types.Trade Data.Scientific.Scientific
+ Network.Bitcoin.BitX.Types: instance Network.Bitcoin.BitX.Types.HasTimestamp Network.Bitcoin.BitX.Types.Orderbook Data.Time.Clock.Internal.UTCTime.UTCTime
+ Network.Bitcoin.BitX.Types: instance Network.Bitcoin.BitX.Types.HasTimestamp Network.Bitcoin.BitX.Types.PrivateTrade Data.Time.Clock.Internal.UTCTime.UTCTime
+ Network.Bitcoin.BitX.Types: instance Network.Bitcoin.BitX.Types.HasTimestamp Network.Bitcoin.BitX.Types.Ticker Data.Time.Clock.Internal.UTCTime.UTCTime
+ Network.Bitcoin.BitX.Types: instance Network.Bitcoin.BitX.Types.HasTimestamp Network.Bitcoin.BitX.Types.Trade Data.Time.Clock.Internal.UTCTime.UTCTime
+ Network.Bitcoin.BitX.Types: instance Network.Bitcoin.BitX.Types.HasTimestamp Network.Bitcoin.BitX.Types.Transaction Data.Time.Clock.Internal.UTCTime.UTCTime
- Network.Bitcoin.BitX.Types: Order :: Scientific -> Int -> Order
+ Network.Bitcoin.BitX.Types: Order :: Scientific -> Scientific -> Order
- Network.Bitcoin.BitX.Types: OrderRequest :: CcyPair -> OrderType -> Scientific -> Int -> OrderRequest
+ Network.Bitcoin.BitX.Types: OrderRequest :: CcyPair -> OrderType -> Scientific -> Scientific -> OrderRequest
- Network.Bitcoin.BitX.Types: PrivateOrder :: Scientific -> Scientific -> UTCTime -> UTCTime -> UTCTime -> Scientific -> Scientific -> Int -> Scientific -> OrderID -> CcyPair -> RequestStatus -> OrderType -> PrivateOrder
+ Network.Bitcoin.BitX.Types: PrivateOrder :: Scientific -> Scientific -> UTCTime -> UTCTime -> UTCTime -> Scientific -> Scientific -> Scientific -> Scientific -> OrderID -> CcyPair -> RequestStatus -> OrderType -> PrivateOrder
- Network.Bitcoin.BitX.Types: PrivateTrade :: Scientific -> Scientific -> Scientific -> Scientific -> Bool -> Text -> CcyPair -> Int -> UTCTime -> OrderType -> Scientific -> PrivateTrade
+ Network.Bitcoin.BitX.Types: PrivateTrade :: Scientific -> Scientific -> Scientific -> Scientific -> Bool -> Text -> CcyPair -> Scientific -> UTCTime -> OrderType -> Scientific -> PrivateTrade
- Network.Bitcoin.BitX.Types: Ticker :: UTCTime -> Maybe Int -> Maybe Int -> Maybe Int -> Scientific -> CcyPair -> Ticker
+ Network.Bitcoin.BitX.Types: Ticker :: UTCTime -> Maybe Scientific -> Maybe Scientific -> Maybe Scientific -> Scientific -> CcyPair -> Ticker
- Network.Bitcoin.BitX.Types: Trade :: UTCTime -> Scientific -> Int -> Bool -> Trade
+ Network.Bitcoin.BitX.Types: Trade :: UTCTime -> Scientific -> Scientific -> Bool -> Trade
- Network.Bitcoin.BitX.Types: [orderPrice] :: Order -> Int
+ Network.Bitcoin.BitX.Types: [orderPrice] :: Order -> Scientific
- Network.Bitcoin.BitX.Types: [orderRequestPrice] :: OrderRequest -> Int
+ Network.Bitcoin.BitX.Types: [orderRequestPrice] :: OrderRequest -> Scientific
- Network.Bitcoin.BitX.Types: [privateOrderLimitPrice] :: PrivateOrder -> Int
+ Network.Bitcoin.BitX.Types: [privateOrderLimitPrice] :: PrivateOrder -> Scientific
- Network.Bitcoin.BitX.Types: [privateTradePrice] :: PrivateTrade -> Int
+ Network.Bitcoin.BitX.Types: [privateTradePrice] :: PrivateTrade -> Scientific
- Network.Bitcoin.BitX.Types: [tickerAsk] :: Ticker -> Maybe Int
+ Network.Bitcoin.BitX.Types: [tickerAsk] :: Ticker -> Maybe Scientific
- Network.Bitcoin.BitX.Types: [tickerBid] :: Ticker -> Maybe Int
+ Network.Bitcoin.BitX.Types: [tickerBid] :: Ticker -> Maybe Scientific
- Network.Bitcoin.BitX.Types: [tickerLastTrade] :: Ticker -> Maybe Int
+ Network.Bitcoin.BitX.Types: [tickerLastTrade] :: Ticker -> Maybe Scientific
- Network.Bitcoin.BitX.Types: [tradePrice] :: Trade -> Int
+ Network.Bitcoin.BitX.Types: [tradePrice] :: Trade -> Scientific
Files
- CHANGES +7/−1
- README.md +6/−6
- bitx-bitcoin.cabal +6/−6
- src/Network/Bitcoin/BitX.hs +0/−3
- src/Network/Bitcoin/BitX/Internal.hs +12/−12
- src/Network/Bitcoin/BitX/Private/Fees.hs +1/−1
- src/Network/Bitcoin/BitX/Types.hs +17/−29
- src/Network/Bitcoin/BitX/Types/Internal.hs +20/−34
- test/Example.hs +1/−1
- test/Network/Bitcoin/BitX/Spec/Specs/AesonRecordSpec.hs +26/−16
- test/Network/Bitcoin/BitX/Spec/Specs/JsonSpec.hs +8/−16
- test/Network/Bitcoin/BitX/Spec/Specs/NetSpec.hs +1/−1
- test/Network/Bitcoin/BitX/Spec/Specs/PostSpec.hs +2/−2
CHANGES view
@@ -1,3 +1,10 @@+v0.12.0.0+* We now handle nulls properly when deserialising JSON arrays.+* Luno has added Ethereum and an ETHXBT currency pair.+* Prices (bid, ask, and spot) are now consistently Scientific, rather than Int. BREAKING CHANGE.+* We no longer create a brand new connection manager on every API call, significatly improving performance.+* Luno's rate-limiting is now much more harsh.+ v0.11.0.1 * Added new fee_info endpoint. @@ -73,4 +80,3 @@ v0.1.0.0 * Initial release!-
README.md view
@@ -2,10 +2,10 @@ [](https://hackage.haskell.org/package/bitx-bitcoin) [](http://opensource.org/licenses/BSD-3-Clause) -(Hopefully useful) Haskell bindings to the [BitX](https://bitx.co/) bitcoin exchange's [API](https://bitx.co/api).+(Hopefully useful) Haskell bindings to the [Luno](https://www.luno.com/) (formerly BitX) cryptocurrency exchange's [API](https://www.luno.com/en/api). -As a minimal example, to get the current selling price (in South African Rand) of bitcoin on the-BitX exchange, do the following:+As a minimal example, to get the current selling price (in South African Rand) of Bitcoin on the+Luno exchange, do the following: ```haskell import Control.Lens ((^.))@@ -22,7 +22,7 @@ ValidResponse tic -> case tic ^. BitX.ask of Nothing -> putStrLn "The BTC-ZAR exchange not currently have an ask price..."- Just p -> putStrLn ("1 bitcoin will set you back ZAR" ++ show p ++ ".00.")+ Just p -> putStrLn ("1 Bitcoin will set you back ZAR" ++ show p ++ ".") ErrorResponse err -> error $ "BitX error received: \"" ++ unpack (err ^. BitX.error) ++ "\"" ExceptionResponse ex ->@@ -37,5 +37,5 @@ [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).+more work due to the ``Network`` library not building on Windows out of the box. Your best bet might be to just+use Stack.
bitx-bitcoin.cabal view
@@ -1,14 +1,15 @@ name: bitx-bitcoin-version: 0.11.0.1+version: 0.12.0.0 synopsis: A Haskell library for working with the BitX bitcoin exchange. description:- Haskell bindings to the BitX REST API, as described here: <https://bitx.co/api>.+ Haskell bindings to the Luno (formerly BitX) REST API, as described here:+ <https://www.luno.com/en/api>. . Note that since this library interfaces directly with a financial API, great care must be taken in its use. In particular, the author cannot be held accountable for any financial losses as a result of programming error, whether that error is in your code,- the code of the author of this library, or BitX's code. This is just common sense.+ the code of the author of this library, or Luno's code. This is just common sense. . If you need to make sure that nothing funny happens in the code, apart from reading the source yourself, you should also perform a few test transactions with very small@@ -20,7 +21,7 @@ license-file: LICENSE -author: Tebello Thejane+author: Tebello Thejane, Tristan Seligmann maintainer: Tebello Thejane <zyxoas+hackage@gmail.com> @@ -36,7 +37,7 @@ homepage: https://github.com/tebello-thejane/bitx.hs -tested-with: GHC >=7.8.1 && <=8.0.1+tested-with: GHC >=7.8.1 && <=8.2.2 -------------------------------------------------------------------------------- @@ -47,7 +48,6 @@ Network.Bitcoin.BitX.Private Network.Bitcoin.BitX.Types Network.Bitcoin.BitX.Private.Quote- -- Network.Bitcoin.BitX.Private.Auth Network.Bitcoin.BitX.Private.Order Network.Bitcoin.BitX.Private.Withdrawal Network.Bitcoin.BitX.Private.Fees
src/Network/Bitcoin/BitX.hs view
@@ -20,7 +20,6 @@ module Network.Bitcoin.BitX.Private, module Network.Bitcoin.BitX.Public, module Network.Bitcoin.BitX.Private.Quote,- --module Network.Bitcoin.BitX.Private.Auth, module Network.Bitcoin.BitX.Private.Order, module Network.Bitcoin.BitX.Private.Withdrawal, module Network.Bitcoin.BitX.Private.Fees,@@ -34,7 +33,6 @@ import Network.Bitcoin.BitX.Private import Network.Bitcoin.BitX.Public import Network.Bitcoin.BitX.Private.Quote---import Network.Bitcoin.BitX.Private.Auth import Network.Bitcoin.BitX.Private.Order import Network.Bitcoin.BitX.Private.Withdrawal import Network.Bitcoin.BitX.Private.Fees@@ -42,4 +40,3 @@ import Network.Bitcoin.BitX.Response {-# ANN module "HLint: ignore Use import/export shortcut" #-}-
src/Network/Bitcoin/BitX/Internal.hs view
@@ -15,7 +15,7 @@ import Network.Bitcoin.BitX.Types.Internal import qualified Network.HTTP.Client as NetCon import qualified Network.HTTP.Client.TLS as NetCon-import Network.HTTP.Types.Status (status503, status429)+import Network.HTTP.Types.Status (status429) import Network.HTTP.Client (Response(..), Request(..)) import Control.Exception (try) import qualified Data.Aeson as Aeson (decode, eitherDecode)@@ -33,6 +33,7 @@ import Data.Text.Encoding (decodeUtf8) import Data.Monoid ((<>)) import Control.Monad.Catch (MonadThrow)+import System.IO.Unsafe (unsafePerformIO) #if __GLASGOW_HASKELL__ >= 710 -- <$> is in base since 4.8 (GHC 7.10) due to the AMP import Control.Applicative ((<|>))@@ -46,8 +47,9 @@ bitXAPIRoot :: Text bitXAPIRoot = bitXAPIPrefix <> "1/" -globalManager :: IO NetCon.Manager-globalManager = NetCon.newManager NetCon.tlsManagerSettings+globalManager :: NetCon.Manager+globalManager = unsafePerformIO (NetCon.newManager NetCon.tlsManagerSettings)+{-# NOINLINE globalManager #-} psUrl :: MonadThrow m => String -> m Request #if MIN_VERSION_http_client(0,4,30)@@ -57,9 +59,8 @@ #endif authConnect :: BitXAuth -> Request -> IO (Either NetCon.HttpException (Response BL.ByteString))-authConnect auth req = do- manager <- globalManager- try . flip NetCon.httpLbs manager . NetCon.applyBasicAuth userID userSecret $ req+authConnect auth =+ try . flip NetCon.httpLbs globalManager . NetCon.applyBasicAuth userID userSecret where userID = Txt.encodeUtf8 (auth ^. Types.id) userSecret = Txt.encodeUtf8 (auth ^. Types.secret)@@ -88,10 +89,9 @@ consumeResponseIO simpleBitXGet_ :: BitXAesRecordConvert recd => Text -> IO (BitXAPIResponse recd)-simpleBitXGet_ verb = withSocketsDo $ do- manager <- globalManager+simpleBitXGet_ verb = withSocketsDo $ rateLimit- (try . flip NetCon.httpLbs manager . fromJust . psUrl $ Txt.unpack (bitXAPIRoot <> verb))+ (try . flip NetCon.httpLbs globalManager . fromJust . psUrl $ Txt.unpack (bitXAPIRoot <> verb)) consumeResponseIO rateLimit :: IO (Either NetCon.HttpException c) -> (Either NetCon.HttpException c -> IO d) -> IO d@@ -106,7 +106,7 @@ threadDelay del go (incDelay del) else act2 resp- maxLimit = 5 * 1000 * 1000 -- 5 seconds probably means something else is wrong...+ maxLimit = 30 * 1000 * 1000 -- 30 seconds probably means something else is wrong... incDelay = round . (* (1.5 :: Double)) . fromIntegral consumeResponseIO :: BitXAesRecordConvert recd => Either NetCon.HttpException (NetCon.Response BL.ByteString)@@ -136,8 +136,8 @@ isRateLimited :: Either NetCon.HttpException a -> Bool #if MIN_VERSION_http_client(0,5,0)-isRateLimited (Left (NetCon.HttpExceptionRequest _ (NetCon.StatusCodeException r _ ))) = (responseStatus r == status503) || (responseStatus r == status429)+isRateLimited (Left (NetCon.HttpExceptionRequest _ (NetCon.StatusCodeException r _ ))) = responseStatus r == status429 #else-isRateLimited (Left (NetCon.StatusCodeException st _ _)) = st == status503 || st == status429+isRateLimited (Left (NetCon.StatusCodeException st _ _)) = st == status429 #endif isRateLimited _ = False
src/Network/Bitcoin/BitX/Private/Fees.hs view
@@ -13,7 +13,7 @@ -- Retrieving fee information. -- -- The BitX fee structure is cummulative-volume-based. The sole endpoint in--- this packagereturns the user's current fee structure and 30-day volume.+-- this package returns the user's current fee structure and 30-day volume. -- -----------------------------------------------------------------------------
src/Network/Bitcoin/BitX/Types.hs view
@@ -185,19 +185,10 @@ -- | A currency pair data CcyPair = XBTZAR -- ^ Bitcoin vs. ZAR- | XBTNAD -- ^ Bitcoin vs. Namibian Dollar- | ZARXBT -- ^ ZAR vs. Namibian Dollar- | NADXBT -- ^ Namibian Dollar vs. Bitcoin- | XBTKES -- ^ Bitcoin vs. Kenyan Shilling- | KESXBT -- ^ Kenyan Shilling vs Bitcoin | XBTMYR -- ^ Bitcoin vs. Malaysian Ringgit- | MYRXBT -- ^ Malaysian Ringgit vs. Bitcoin | XBTNGN -- ^ Bitcoin vs. Nigerian Naira- | NGNXBT -- ^ Nigerian Naira vs. Bitcoin | XBTIDR -- ^ Bitcoin vs. Indonesian Rupiah- | IDRXBT -- ^ Indonesian Rupiah vs. Bitcoin- | XBTSGD -- ^ Bitcoin vs. Singapore Dollar- | SGDXBT -- ^ Singapore Dollar vs. Bitcoin+ | ETHXBT -- ^ Ether vs. Bitcoin deriving (Show, Generic, Eq, Data, Typeable, Ord) instance NFData CcyPair@@ -207,9 +198,9 @@ -- the price of the last filled bid order. Necessarily @bid <= ask.@ data Ticker = Ticker { tickerTimestamp :: UTCTime,- tickerBid :: Maybe Int,- tickerAsk :: Maybe Int,- tickerLastTrade :: Maybe Int,+ tickerBid :: Maybe Scientific,+ tickerAsk :: Maybe Scientific,+ tickerLastTrade :: Maybe Scientific, tickerRolling24HourVolume :: Scientific, tickerPair :: CcyPair } deriving (Eq, Show, Generic, Data, Typeable, Ord)@@ -221,13 +212,11 @@ -- | A trade-able asset. Essentially, a currency. data Asset = ZAR -- ^ South African Rand- | NAD -- ^ Namibian Dollar | XBT -- ^ Bitcoin- | KES -- ^ Kenyan Shilling | MYR -- ^ Malaysian Ringgit- | NGN -- ^ Nigerian Naira | IDR -- ^ Indonesian Rupiah- | SGD -- ^ Singapore Dollar+ | BCH -- ^ Bitcoin Cash+ | ETH -- ^ Ether deriving (Show, Generic, Eq, Data, Typeable, Ord) instance NFData Asset@@ -252,7 +241,7 @@ -- | A single placed order in the orderbook data Order = Order { orderVolume :: Scientific,- orderPrice :: Int+ orderPrice :: Scientific } deriving (Eq, Show, Generic, Data, Typeable, Ord) instance NFData Order@@ -280,7 +269,7 @@ data Trade = Trade { tradeTimestamp :: UTCTime, tradeVolume :: Scientific,- tradePrice :: Int,+ tradePrice :: Scientific, tradeIsBuy :: Bool } deriving (Eq, Show, Generic, Data, Typeable, Ord) @@ -326,7 +315,7 @@ privateOrderCompletedTimestamp :: UTCTime, privateOrderFeeBase :: Scientific, privateOrderFeeCounter :: Scientific,- privateOrderLimitPrice :: Int,+ privateOrderLimitPrice :: Scientific, privateOrderLimitVolume :: Scientific, privateOrderId :: OrderID, privateOrderPair :: CcyPair,@@ -357,15 +346,15 @@ {orderRequestPair :: CcyPair, orderRequestOrderType :: OrderType, orderRequestVolume :: Scientific,- orderRequestPrice :: Int } deriving (Eq, Show, Generic, Data, Typeable, Ord)+ orderRequestPrice :: Scientific } deriving (Eq, Show, Generic, Data, Typeable, Ord) instance NFData OrderRequest makeFields ''OrderRequest --- |@mkOrderRequest = OrderRequest ZARXBT BID 0 0@+-- |@mkOrderRequest = OrderRequest XBTZAR BID 0 0@ mkOrderRequest :: OrderRequest-mkOrderRequest = OrderRequest ZARXBT BID 0 0+mkOrderRequest = OrderRequest XBTZAR BID 0 0 data MarketOrderRequest = MarketOrderRequest {marketOrderRequestPair :: CcyPair,@@ -376,9 +365,9 @@ makeFields ''MarketOrderRequest --- |@mkMarketOrderRequest = MarketOrderRequest ZARXBT BID 0@+-- |@mkMarketOrderRequest = MarketOrderRequest XBTZAR BID 0@ mkMarketOrderRequest :: MarketOrderRequest-mkMarketOrderRequest = MarketOrderRequest ZARXBT BID 0+mkMarketOrderRequest = MarketOrderRequest XBTZAR BID 0 -- | The current balance of a private account. data Balance = Balance@@ -452,9 +441,9 @@ makeFields ''QuoteRequest --- |@mkQuoteRequest = QuoteRequest BUY ZARXBT 0@+-- |@mkQuoteRequest = QuoteRequest BUY XBTZAR 0@ mkQuoteRequest :: QuoteRequest-mkQuoteRequest = QuoteRequest BUY ZARXBT 0+mkQuoteRequest = QuoteRequest BUY XBTZAR 0 -- | A temporarily locked-in quote. data OrderQuote = OrderQuote@@ -496,7 +485,7 @@ privateTradeIsBuy :: Bool, privateTradeOrderId :: Text, privateTradePair :: CcyPair,- privateTradePrice :: Int,+ privateTradePrice :: Scientific, privateTradeTimestamp :: UTCTime, privateTradeOrderType :: OrderType, privateTradeVolume :: Scientific@@ -524,4 +513,3 @@ instance FromJSON WithdrawalType instance FromJSON QuoteType-
src/Network/Bitcoin/BitX/Types/Internal.hs view
@@ -29,10 +29,10 @@ import Data.Scientific (Scientific) import Data.ByteString (ByteString) import Data.List.Split (splitOn)-import qualified Data.ByteString.Char8 as BS8 (pack) #if __GLASGOW_HASKELL__ >= 708 import Data.Coerce #endif+import Data.Maybe (fromMaybe) import Test.QuickCheck {-# ANN module ("HLint: ignore Use camelCase" :: String) #-}@@ -78,18 +78,12 @@ -- | Wrappers around Scientific and Int, and FromJSON instance, to facilitate automatic JSON instances newtype QuotedScientific = QuotedScientific Scientific deriving (Read, Show)-newtype QuotedInt = QuotedInt Int deriving (Read, Show) instance FromJSON QuotedScientific where parseJSON (String x) = return . QuotedScientific . read . Txt.unpack $ x parseJSON (Number x) = return . QuotedScientific . read . show $ x parseJSON _ = mempty -instance FromJSON QuotedInt where- 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- qsToScientific :: QuotedScientific -> Scientific #if __GLASGOW_HASKELL__ >= 708 qsToScientific = coerce@@ -98,14 +92,6 @@ qsToScientific (QuotedScientific sci) = sci #endif -qiToInt :: QuotedInt -> Int-#if __GLASGOW_HASKELL__ >= 708-qiToInt = coerce-{-# INLINE qiToInt #-}-#else-qiToInt (QuotedInt i) = i-#endif- -- | Wrapper around UTCTime and FromJSON instance, to facilitate automatic JSON instances newtype TimestampMS = TimestampMS Integer deriving (Read, Show)@@ -154,9 +140,9 @@ data Ticker_ = Ticker_ { ticker'timestamp :: TimestampMS- , ticker'bid :: Maybe QuotedInt- , ticker'ask :: Maybe QuotedInt- , ticker'last_trade :: Maybe QuotedInt+ , ticker'bid :: Maybe QuotedScientific+ , ticker'ask :: Maybe QuotedScientific+ , ticker'last_trade :: Maybe QuotedScientific , ticker'rolling_24_hour_volume :: QuotedScientific , ticker'pair :: Types.CcyPair }@@ -168,9 +154,9 @@ type Aes Types.Ticker = Ticker_ aesToRec Ticker_ {..} = Types.Ticker {tickerTimestamp = tsmsToUTCTime ticker'timestamp,- tickerBid = fmap qiToInt ticker'bid,- tickerAsk = fmap qiToInt ticker'ask,- tickerLastTrade = fmap qiToInt ticker'last_trade,+ tickerBid = fmap qsToScientific ticker'bid,+ tickerAsk = fmap qsToScientific ticker'ask,+ tickerLastTrade = fmap qsToScientific ticker'last_trade, tickerRolling24HourVolume = qsToScientific ticker'rolling_24_hour_volume, tickerPair = ticker'pair} @@ -206,7 +192,7 @@ data Order_ = Order_ { order'volume :: QuotedScientific,- order'price :: QuotedInt+ order'price :: QuotedScientific } $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''Order_)@@ -215,7 +201,7 @@ type Aes Types.Order = Order_ aesToRec Order_ {..} = Types.Order {orderVolume = qsToScientific order'volume,- orderPrice = qiToInt order'price}+ orderPrice = qsToScientific order'price} -------------------------------------------- Orderbook type ---------------------------------------- @@ -243,7 +229,7 @@ data Trade_ = Trade_ { trade'volume :: QuotedScientific , trade'timestamp :: TimestampMS- , trade'price :: QuotedInt+ , trade'price :: QuotedScientific , trade'is_buy :: Bool } @@ -255,7 +241,7 @@ aesToRec Trade_ {..} = Types.Trade { tradeTimestamp = tsmsToUTCTime trade'timestamp, tradeVolume = qsToScientific trade'volume,- tradePrice = qiToInt trade'price,+ tradePrice = qsToScientific trade'price, tradeIsBuy = trade'is_buy} ----------------------------------------- PublicTrades type ----------------------------------------@@ -282,7 +268,7 @@ , privateOrder'completed_timestamp :: TimestampMS , privateOrder'fee_base :: QuotedScientific , privateOrder'fee_counter :: QuotedScientific- , privateOrder'limit_price :: QuotedInt+ , privateOrder'limit_price :: QuotedScientific , privateOrder'limit_volume :: QuotedScientific , privateOrder'order_id :: Types.OrderID , privateOrder'pair :: Types.CcyPair@@ -303,7 +289,7 @@ privateOrderCompletedTimestamp = tsmsToUTCTime privateOrder'completed_timestamp, privateOrderFeeBase = qsToScientific privateOrder'fee_base, privateOrderFeeCounter = qsToScientific privateOrder'fee_counter,- privateOrderLimitPrice = qiToInt privateOrder'limit_price,+ privateOrderLimitPrice = qsToScientific privateOrder'limit_price, privateOrderLimitVolume = qsToScientific privateOrder'limit_volume, privateOrderId = privateOrder'order_id, privateOrderPair = privateOrder'pair,@@ -313,7 +299,7 @@ ------------------------------------------ PrivateOrders type -------------------------------------- data PrivateOrders_ = PrivateOrders_- {privateOrders'orders :: [PrivateOrder_]+ {privateOrders'orders :: Maybe [PrivateOrder_] } $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"}@@ -322,7 +308,7 @@ instance BitXAesRecordConvert [Types.PrivateOrder] where type Aes [Types.PrivateOrder] = PrivateOrders_ aesToRec PrivateOrders_ {..} =- map aesToRec privateOrders'orders+ map aesToRec (fromMaybe [] privateOrders'orders) ------------------------------------------ OrderRequest type --------------------------------------- @@ -331,7 +317,7 @@ [("pair", showableToBytestring_ (oreq ^. Types.pair)), ("type", showableToBytestring_ (oreq ^. Types.orderType)), ("volume", realToDecimalByteString_ (oreq ^. Types.volume)),- ("price", BS8.pack . show $ (oreq ^. Types.price))]+ ("price", realToDecimalByteString_ (oreq ^. Types.price))] -------------------------------------------- OrderIDRec type --------------------------------------- @@ -631,7 +617,7 @@ , privateTrade'is_buy :: Bool , privateTrade'order_id :: Types.OrderID , privateTrade'pair :: Types.CcyPair- , privateTrade'price :: QuotedInt+ , privateTrade'price :: QuotedScientific , privateTrade'timestamp :: TimestampMS , privateTrade'type :: OrderType_ , privateTrade'volume :: QuotedScientific@@ -650,7 +636,7 @@ privateTradeIsBuy = privateTrade'is_buy, privateTradeOrderId = privateTrade'order_id, privateTradePair = privateTrade'pair,- privateTradePrice = qiToInt privateTrade'price,+ privateTradePrice = qsToScientific privateTrade'price, privateTradeTimestamp = tsmsToUTCTime privateTrade'timestamp, privateTradeOrderType = orderTypeParse privateTrade'type, privateTradeVolume = qsToScientific privateTrade'volume}@@ -658,7 +644,7 @@ ----------------------------------------- PrivateTrades type ---------------------------------------- data PrivateTrades_ = PrivateTrades_- { privateTrades'trades :: [PrivateTrade_]+ { privateTrades'trades :: Maybe [PrivateTrade_] } $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"}@@ -667,7 +653,7 @@ instance BitXAesRecordConvert [Types.PrivateTrade] where type Aes [Types.PrivateTrade] = PrivateTrades_ aesToRec PrivateTrades_ {..} =- map aesToRec privateTrades'trades+ map aesToRec (fromMaybe [] privateTrades'trades) -------------------------------------------- FeeInfo type -------------------------------------------
test/Example.hs view
@@ -12,7 +12,7 @@ ValidResponse tic -> case tic ^. BitX.ask of Nothing -> putStrLn "The BTC-ZAR exchange not currently have an ask price..."- Just p -> putStrLn ("1 bitcoin will set you back ZAR" ++ show p ++ ".00.")+ Just p -> putStrLn ("1 bitcoin will set you back ZAR" ++ show p ++ ".") ErrorResponse err -> error $ "BitX error received: \"" ++ unpack (err ^. BitX.error) ++ "\"" ExceptionResponse ex ->
test/Network/Bitcoin/BitX/Spec/Specs/AesonRecordSpec.hs view
@@ -85,7 +85,7 @@ recordAesCheck "{\"base\":\"568.7\", \"counter\":3764.2,\"creation_timestamp\":478873467, \ \ \"expiration_timestamp\":8768834222, \"completed_timestamp\":6511257825, \"fee_base\":\"3687.3\", \"fee_counter\":12.9,\- \ \"limit_price\":765.00,\"limit_volume\":55.2,\"order_id\":\"83YG\",\"pair\":\"NADXBT\",\+ \ \"limit_price\":765.00,\"limit_volume\":55.2,\"order_id\":\"83YG\",\"pair\":\"XBTMYR\",\ \ \"state\":\"COMPLETE\",\"type\":\"BID\"}" PrivateOrder {privateOrderBase = 568.7,@@ -98,16 +98,21 @@ privateOrderLimitPrice = 765, privateOrderLimitVolume = 55.2, privateOrderId = "83YG",- privateOrderPair = NADXBT,+ privateOrderPair = XBTMYR, privateOrderState = COMPLETE, privateOrderOrderType = BID}- it "PrivateOrders is parsed properly" $- recordAesCheck- "{\"orders\":[{\"base\":\"568.7\", \"counter\":3764.2,\"creation_timestamp\":478873467, \- \ \"expiration_timestamp\":8768834222, \"completed_timestamp\":6511257825, \"fee_base\":\"3687.3\", \"fee_counter\":12.9,\- \ \"limit_price\":765.00,\"limit_volume\":55.2,\"order_id\":\"83YG\",\"pair\":\"NADXBT\",\- \ \"state\":\"COMPLETE\",\"type\":\"BID\"}]}"- [privateOrderInner]+ describe "PrivateOrders" $ do+ it "PrivateOrders is parsed properly" $+ recordAesCheck+ "{\"orders\":[{\"base\":\"568.7\", \"counter\":3764.2,\"creation_timestamp\":478873467, \+ \ \"expiration_timestamp\":8768834222, \"completed_timestamp\":6511257825, \"fee_base\":\"3687.3\", \"fee_counter\":12.9,\+ \ \"limit_price\":765.00,\"limit_volume\":55.2,\"order_id\":\"83YG\",\"pair\":\"XBTMYR\",\+ \ \"state\":\"COMPLETE\",\"type\":\"BID\"}]}"+ [privateOrderInner]+ it "parses null as an empty list" $+ recordAesCheck+ "{\"orders\":null}"+ ([] :: [PrivateOrder]) it "OrderID is parsed properly" $ recordAesCheck "{\"order_id\":\"57983\"}"@@ -173,12 +178,17 @@ privateTradeTimestamp = posixSecondsToUTCTime 1467138492.909, privateTradeOrderType = BID, privateTradeVolume = 0.147741}- it "PrivateTrades is parsed properly" $- recordAesCheck- "{\"trades\":[{\"base\": \"0.147741\", \"counter\": \"1549.950831\", \"fee_base\": \"0.90\", \"fee_counter\": \"0.00\", \- \ \"is_buy\": false, \"order_id\": \"BXMC2CJ7HNB88U4\", \"pair\": \"XBTZAR\", \"price\": \"10491.00\", \- \ \"timestamp\": 1467138492909, \"type\": \"BID\", \"volume\": \"0.147741\" }]}"- [privateTradeInner]+ describe "PrivateTrades" $ do+ it "parses a canned example correctly" $+ recordAesCheck+ "{\"trades\":[{\"base\": \"0.147741\", \"counter\": \"1549.950831\", \"fee_base\": \"0.90\", \"fee_counter\": \"0.00\", \+ \ \"is_buy\": false, \"order_id\": \"BXMC2CJ7HNB88U4\", \"pair\": \"XBTZAR\", \"price\": \"10491.00\", \+ \ \"timestamp\": 1467138492909, \"type\": \"BID\", \"volume\": \"0.147741\" }]}"+ [privateTradeInner]+ it "parses null as an empty list" $+ recordAesCheck+ "{\"trades\":null}"+ ([] :: [PrivateTrade]) it "FeeInfo is parsed properly" $ recordAesCheck "{\"maker_fee\": \"0.00\", \"taker_fee\": \"0.10\", \"thirty_day_volume\": \"0.894342\"}"@@ -221,7 +231,7 @@ privateOrderLimitPrice = 765, privateOrderLimitVolume = 55.2, privateOrderId = "83YG",- privateOrderPair = NADXBT,+ privateOrderPair = XBTMYR, privateOrderState = COMPLETE, privateOrderOrderType = BID}
test/Network/Bitcoin/BitX/Spec/Specs/JsonSpec.hs view
@@ -29,19 +29,11 @@ recordAesCheck "{\"volume\":314159,\"price\":\"4321\"}" (Order 314159 4321)- it "QuotedInt is parsed when quoted" $- recordAesCheck- "{\"volume\":0,\"price\":\"4321.00\"}"- (Order 0 4321)- it "QuotedInt is parsed when not quoted" $- recordAesCheck- "{\"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, \"completed_timestamp\":6511257825, \"fee_base\":\"3687.3\", \"fee_counter\":12.9,\- \\"limit_price\":765.00,\"limit_volume\":55.2,\"order_id\":\"83YG\",\"pair\":\"NADXBT\",\+ \\"limit_price\":765.00,\"limit_volume\":55.2,\"order_id\":\"83YG\",\"pair\":\"XBTMYR\",\ \\"state\":\"COMPLETE\",\"type\":\"BUY\"}" PrivateOrder {privateOrderBase = 568.7,@@ -54,14 +46,14 @@ privateOrderLimitPrice = 765, privateOrderLimitVolume = 55.2, privateOrderId = "83YG",- privateOrderPair = NADXBT,+ privateOrderPair = XBTMYR, privateOrderState = COMPLETE, privateOrderOrderType = BID } it "OrderType BID is parsed properly" $ recordAesCheck "{\"base\":\"568.7\", \"counter\":3764.2,\"creation_timestamp\":478873467, \ \\"expiration_timestamp\":8768834222, \"completed_timestamp\":6511257825, \"fee_base\":\"3687.3\", \"fee_counter\":12.9,\- \\"limit_price\":765.00,\"limit_volume\":55.2,\"order_id\":\"83YG\",\"pair\":\"NADXBT\",\+ \\"limit_price\":765.00,\"limit_volume\":55.2,\"order_id\":\"83YG\",\"pair\":\"XBTMYR\",\ \\"state\":\"COMPLETE\",\"type\":\"BID\"}" PrivateOrder {privateOrderBase = 568.7,@@ -74,14 +66,14 @@ privateOrderLimitPrice = 765, privateOrderLimitVolume = 55.2, privateOrderId = "83YG",- privateOrderPair = NADXBT,+ privateOrderPair = XBTMYR, privateOrderState = COMPLETE, privateOrderOrderType = BID } it "OrderType ASK is parsed properly" $ recordAesCheck "{\"base\":\"568.7\", \"counter\":3764.2,\"creation_timestamp\":478873467, \ \\"expiration_timestamp\":8768834222, \"completed_timestamp\":6511257825, \"fee_base\":\"3687.3\", \"fee_counter\":12.9,\- \\"limit_price\":765.00,\"limit_volume\":55.2,\"order_id\":\"83YG\",\"pair\":\"NADXBT\",\+ \\"limit_price\":765.00,\"limit_volume\":55.2,\"order_id\":\"83YG\",\"pair\":\"XBTMYR\",\ \\"state\":\"COMPLETE\",\"type\":\"ASK\"}" PrivateOrder {privateOrderBase = 568.7,@@ -94,14 +86,14 @@ privateOrderLimitPrice = 765, privateOrderLimitVolume = 55.2, privateOrderId = "83YG",- privateOrderPair = NADXBT,+ privateOrderPair = XBTMYR, privateOrderState = COMPLETE, privateOrderOrderType = ASK } it "OrderType SELL is parsed properly" $ recordAesCheck "{\"base\":\"568.7\", \"counter\":3764.2,\"creation_timestamp\":478873467, \ \\"expiration_timestamp\":8768834222, \"completed_timestamp\":6511257825, \"fee_base\":\"3687.3\", \"fee_counter\":12.9,\- \\"limit_price\":765.00,\"limit_volume\":55.2,\"order_id\":\"83YG\",\"pair\":\"NADXBT\",\+ \\"limit_price\":765.00,\"limit_volume\":55.2,\"order_id\":\"83YG\",\"pair\":\"XBTMYR\",\ \\"state\":\"COMPLETE\",\"type\":\"SELL\"}" PrivateOrder {privateOrderBase = 568.7,@@ -114,7 +106,7 @@ privateOrderLimitPrice = 765, privateOrderLimitVolume = 55.2, privateOrderId = "83YG",- privateOrderPair = NADXBT,+ privateOrderPair = XBTMYR, privateOrderState = COMPLETE, privateOrderOrderType = ASK } it "RequestStatus COMPLETE is parsed properly" $
test/Network/Bitcoin/BitX/Spec/Specs/NetSpec.hs view
@@ -22,7 +22,7 @@ it "getOrderBook connects to BitX and works" $ connectsAndParsesOkay $ getOrderBook XBTZAR it "getTrades connects to BitX and works" $- connectsAndParsesOkay $ getTrades Nothing XBTKES+ connectsAndParsesOkay $ getTrades Nothing XBTMYR connectsAndParsesOkay :: IO (BitXAPIResponse recd) -> Bool connectsAndParsesOkay = isValidResponse . unsafePerformIO
test/Network/Bitcoin/BitX/Spec/Specs/PostSpec.hs view
@@ -64,11 +64,11 @@ postEncode QuoteRequest {quoteRequestQuoteType = BUY,- quoteRequestPair = XBTKES,+ quoteRequestPair = XBTMYR, quoteRequestBaseAmount = 566.76} `shouldBe` [("type", "BUY"),- ("pair", "XBTKES"),+ ("pair", "XBTMYR"), ("base_amount", "566.76")] it "MarketOrderRequest for buy is post-encoded properly" $ postEncode