{-# LANGUAGE QuasiQuotes, OverloadedStrings, TemplateHaskell, TypeFamilies, FlexibleContexts, FlexibleInstances, DataKinds, CPP, RecordWildCards, GeneralizedNewtypeDeriving #-} module Network.Bitcoin.BitX.Types.Internal ( BitXAesRecordConvert(..), POSTEncodeable(..), Transaction_(..), pendingTransactionsToTransactions, timeToTimestamp ) where import Network.Bitcoin.BitX.Types.Internal.Decimal import qualified Network.Bitcoin.BitX.Types as Types import Data.Aeson (FromJSON(..), parseJSON, Value(..)) import qualified Data.Aeson.TH as AesTH import qualified Data.Text as Txt import qualified Data.Text.Encoding as Txt import Data.Text (Text) import Data.Time.Clock (UTCTime) import Data.Time.Clock.POSIX (posixSecondsToUTCTime, utcTimeToPOSIXSeconds) import Lens.Micro ((^.)) #if __GLASGOW_HASKELL__ >= 710 -- base 4.8+ (GHC 7.10+) re-exports Monoid and its functions/constants #else import Data.Monoid (mempty) #endif 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 Test.QuickCheck {-# ANN module ("HLint: ignore Use camelCase" :: String) #-} newtype UnixStampMS = UnixStampMS {_unUnixStampMS :: Integer} deriving (Eq, Ord, Show, Num, Integral, Real, Enum) instance Arbitrary UnixStampMS where arbitrary = do k <- choose (1225497600000, 32782406400000) -- 1 Nov 2008 00:00:00 to 1 Nov 3008 00:00:00 return $ UnixStampMS k timestampParse_ :: Integer -> UTCTime timestampParse_ = posixSecondsToUTCTime . realToFrac . ( / 1000) . (fromIntegral :: Integer -> Scientific) timeToTimestamp :: UTCTime -> Integer timeToTimestamp = truncate . (* 1000). utcTimeToPOSIXSeconds -- $setup -- >>> import Test.QuickCheck -- | -- prop> \ n -> (timeToTimestamp $ timestampParse_ $ _unUnixStampMS n) == _unUnixStampMS n -- ifJustText :: ByteString -> Maybe Text -> [(ByteString, ByteString)] ifJustText lbl (Just a) = [(lbl, Txt.encodeUtf8 a)] ifJustText _ Nothing = [] class FromJSON (Aes recd) => BitXAesRecordConvert recd where type Aes recd aesToRec :: Aes recd -> recd class POSTEncodeable recd where postEncode :: recd -> [(ByteString, ByteString)] showableToBytestring_ :: Show a => a -> ByteString showableToBytestring_ = Txt.encodeUtf8 . Txt.pack . show -- | 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 {-# INLINE qsToScientific #-} #else 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) instance FromJSON TimestampMS where parseJSON (Number x) = return . TimestampMS . round $ x parseJSON _ = mempty --instance ToJSON TimestampMS where -- toJSON (TimestampMS t) = Number . fromIntegral $ t tsmsToUTCTime :: TimestampMS -> UTCTime tsmsToUTCTime (TimestampMS ms) = timestampParse_ ms newtype OrderType_ = OrderType_ Text deriving (Read, Show) instance FromJSON OrderType_ where parseJSON (String x) = return . OrderType_ $ x parseJSON _ = mempty orderTypeParse :: OrderType_ -> Types.OrderType orderTypeParse (OrderType_ "BUY") = Types.BID orderTypeParse (OrderType_ "BID") = Types.BID orderTypeParse (OrderType_ "ASK") = Types.ASK orderTypeParse (OrderType_ "SELL") = Types.ASK orderTypeParse (OrderType_ x ) = error $ "Yet another surprise from the BitX API: unexpected OrderType " ++ Txt.unpack x newtype RequestStatus_ = RequestStatus_ Text deriving (Read, Show) instance FromJSON RequestStatus_ where parseJSON (String x) = return . RequestStatus_ $ x parseJSON _ = mempty requestStatusParse :: RequestStatus_ -> Types.RequestStatus requestStatusParse (RequestStatus_ "PENDING") = Types.PENDING requestStatusParse (RequestStatus_ "COMPLETE") = Types.COMPLETE requestStatusParse (RequestStatus_ "COMPLETED") = Types.COMPLETE requestStatusParse (RequestStatus_ "CANCELLED") = Types.CANCELLED requestStatusParse (RequestStatus_ x ) = error $ "Yet another surprise from the BitX API: unexpected RequestStatus " ++ Txt.unpack x -------------------------------------------- Ticker type ------------------------------------------- data Ticker_ = Ticker_ { ticker'timestamp :: TimestampMS , ticker'bid :: Maybe QuotedInt , ticker'ask :: Maybe QuotedInt , ticker'last_trade :: Maybe QuotedInt , ticker'rolling_24_hour_volume :: QuotedScientific , ticker'pair :: Types.CcyPair } $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''Ticker_) instance BitXAesRecordConvert Types.Ticker where 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, tickerRolling24HourVolume = qsToScientific ticker'rolling_24_hour_volume, tickerPair = ticker'pair} --------------------------------------------- Tickers type ----------------------------------------- data Tickers_ = Tickers_ { tickers'tickers :: [Ticker_] } $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''Tickers_) instance BitXAesRecordConvert [Types.Ticker] where type Aes [Types.Ticker] = Tickers_ aesToRec Tickers_ {..} = map aesToRec tickers'tickers -------------------------------------------- BitXError type ---------------------------------------- data BitXError_= BitXError_ { bitXError'error :: Text, bitXError'error_code :: Text } deriving (Show, Eq) $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''BitXError_) instance BitXAesRecordConvert Types.BitXError where type Aes Types.BitXError = BitXError_ aesToRec BitXError_ {..} = Types.BitXError { bitXErrorError = bitXError'error, bitXErrorErrorCode = bitXError'error_code} -------------------------------------------- Order type -------------------------------------------- data Order_ = Order_ { order'volume :: QuotedScientific, order'price :: QuotedInt } $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''Order_) instance BitXAesRecordConvert Types.Order where type Aes Types.Order = Order_ aesToRec Order_ {..} = Types.Order {orderVolume = qsToScientific order'volume, orderPrice = qiToInt order'price} -------------------------------------------- Orderbook type ---------------------------------------- data Orderbook_ = Orderbook_ { orderbook'timestamp :: TimestampMS, orderbook'bids :: [Bid_], orderbook'asks :: [Ask_] } type Bid_ = Order_ type Ask_ = Order_ $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''Orderbook_) instance BitXAesRecordConvert Types.Orderbook where type Aes Types.Orderbook = Orderbook_ aesToRec Orderbook_ {..} = Types.Orderbook {orderbookTimestamp = tsmsToUTCTime orderbook'timestamp, orderbookBids = map aesToRec orderbook'bids, orderbookAsks = map aesToRec orderbook'asks} -------------------------------------------- Trade type -------------------------------------------- data Trade_ = Trade_ { trade'volume :: QuotedScientific , trade'timestamp :: TimestampMS , trade'price :: QuotedInt , trade'is_buy :: Bool } $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''Trade_) instance BitXAesRecordConvert Types.Trade where type Aes Types.Trade = Trade_ aesToRec Trade_ {..} = Types.Trade { tradeTimestamp = tsmsToUTCTime trade'timestamp, tradeVolume = qsToScientific trade'volume, tradePrice = qiToInt trade'price, tradeIsBuy = trade'is_buy} ----------------------------------------- PublicTrades type ---------------------------------------- data PublicTrades_ = PublicTrades_ { publicTrades'trades :: [Trade_] } $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''PublicTrades_) instance BitXAesRecordConvert [Types.Trade] where type Aes [Types.Trade] = PublicTrades_ aesToRec PublicTrades_ {..} = map aesToRec publicTrades'trades ------------------------------------------ PrivateOrder type --------------------------------------- data PrivateOrder_ = PrivateOrder_ { privateOrder'base :: QuotedScientific , privateOrder'counter :: QuotedScientific , privateOrder'creation_timestamp :: TimestampMS , privateOrder'expiration_timestamp :: TimestampMS , privateOrder'completed_timestamp :: TimestampMS , privateOrder'fee_base :: QuotedScientific , privateOrder'fee_counter :: QuotedScientific , privateOrder'limit_price :: QuotedInt , privateOrder'limit_volume :: QuotedScientific , privateOrder'order_id :: Types.OrderID , privateOrder'pair :: Types.CcyPair , privateOrder'state :: RequestStatus_ , privateOrder'type :: OrderType_ } $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''PrivateOrder_) instance BitXAesRecordConvert Types.PrivateOrder where type Aes Types.PrivateOrder = PrivateOrder_ aesToRec PrivateOrder_ {..} = Types.PrivateOrder {privateOrderBase = qsToScientific privateOrder'base, privateOrderCounter = qsToScientific privateOrder'counter, privateOrderCreationTimestamp = tsmsToUTCTime privateOrder'creation_timestamp, privateOrderExpirationTimestamp = tsmsToUTCTime privateOrder'expiration_timestamp, privateOrderCompletedTimestamp = tsmsToUTCTime privateOrder'completed_timestamp, privateOrderFeeBase = qsToScientific privateOrder'fee_base, privateOrderFeeCounter = qsToScientific privateOrder'fee_counter, privateOrderLimitPrice = qiToInt privateOrder'limit_price, privateOrderLimitVolume = qsToScientific privateOrder'limit_volume, privateOrderId = privateOrder'order_id, privateOrderPair = privateOrder'pair, privateOrderState = requestStatusParse privateOrder'state, privateOrderOrderType = orderTypeParse privateOrder'type} ------------------------------------------ PrivateOrders type -------------------------------------- data PrivateOrders_ = PrivateOrders_ {privateOrders'orders :: [PrivateOrder_] } $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''PrivateOrders_) instance BitXAesRecordConvert [Types.PrivateOrder] where type Aes [Types.PrivateOrder] = PrivateOrders_ aesToRec PrivateOrders_ {..} = map aesToRec privateOrders'orders ------------------------------------------ OrderRequest type --------------------------------------- instance POSTEncodeable Types.OrderRequest where postEncode oreq = [("pair", showableToBytestring_ (oreq ^. Types.pair)), ("type", showableToBytestring_ (oreq ^. Types.orderType)), ("volume", realToDecimalByteString_ (oreq ^. Types.volume)), ("price", BS8.pack . show $ (oreq ^. Types.price))] -------------------------------------------- OrderIDRec type --------------------------------------- data OrderIDRec_ = OrderIDRec_ { orderIDResponse'order_id :: Types.OrderID } $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''OrderIDRec_) instance BitXAesRecordConvert Types.OrderID where type Aes Types.OrderID = OrderIDRec_ aesToRec OrderIDRec_ {..} = orderIDResponse'order_id instance POSTEncodeable Types.OrderID where postEncode oid = [("order_id", Txt.encodeUtf8 oid)] ----------------------------------------- RequestSuccess type -------------------------------------- data RequestSuccess_ = RequestSuccess_ { requestSuccess'success :: Bool } $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''RequestSuccess_) instance BitXAesRecordConvert Types.RequestSuccess where type Aes Types.RequestSuccess = RequestSuccess_ aesToRec RequestSuccess_ {..} = requestSuccess'success -------------------------------------------- Balance type ------------------------------------------ data Balance_ = Balance_ { balance'account_id :: Types.AccountID , balance'asset :: Types.Asset , balance'balance :: QuotedScientific , balance'reserved :: QuotedScientific , balance'unconfirmed :: QuotedScientific } $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''Balance_) instance BitXAesRecordConvert Types.Balance where type Aes Types.Balance = Balance_ aesToRec Balance_ {..} = Types.Balance {balanceId = balance'account_id, balanceAsset = balance'asset, balanceBalance = qsToScientific balance'balance, balanceReserved = qsToScientific balance'reserved, balanceUnconfirmed = qsToScientific balance'unconfirmed} -------------------------------------------- Balances type ----------------------------------------- data Balances_ = Balances_ { balances'balance :: [Balance_] } $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''Balances_) instance BitXAesRecordConvert [Types.Balance] where type Aes [Types.Balance] = Balances_ aesToRec Balances_ {..} = map aesToRec balances'balance ----------------------------------------- FundingAddress type -------------------------------------- data FundingAddress_ = FundingAddress_ { fundingAdress'asset :: Types.Asset , fundingAdress'address :: Text , fundingAdress'total_received :: QuotedScientific , fundingAdress'total_unconfirmed :: QuotedScientific } $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''FundingAddress_) instance BitXAesRecordConvert Types.FundingAddress where type Aes Types.FundingAddress = FundingAddress_ aesToRec FundingAddress_ {..} = Types.FundingAddress {fundingAddressAsset = fundingAdress'asset, fundingAddressAddress = fundingAdress'address, fundingAddressTotalReceived = qsToScientific fundingAdress'total_received, fundingAddressTotalUnconfirmed = qsToScientific fundingAdress'total_unconfirmed} --------------------------------------------- Asset type ------------------------------------------- instance POSTEncodeable Types.Asset where postEncode asset = [("asset", showableToBytestring_ asset)] -------------------------------------- WithdrawalRequest type -------------------------------------- data WithdrawalRequest_ = WithdrawalRequest_ { withdrawalRequest'status :: RequestStatus_ , withdrawalRequest'id :: Text } $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''WithdrawalRequest_) instance BitXAesRecordConvert Types.WithdrawalRequest where type Aes Types.WithdrawalRequest = WithdrawalRequest_ aesToRec WithdrawalRequest_ {..} = Types.WithdrawalRequest {withdrawalRequestStatus = requestStatusParse withdrawalRequest'status, withdrawalRequestId = withdrawalRequest'id} -------------------------------------- WithdrawalRequests type ------------------------------------- data WithdrawalRequests_ = WithdrawalRequests_ { withdrawalRequests'withdrawals :: [WithdrawalRequest_] } $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''WithdrawalRequests_) instance BitXAesRecordConvert [Types.WithdrawalRequest] where type Aes [Types.WithdrawalRequest] = WithdrawalRequests_ aesToRec WithdrawalRequests_ {..} = map aesToRec withdrawalRequests'withdrawals ----------------------------------------- NewWithdrawal type --------------------------------------- instance POSTEncodeable Types.NewWithdrawal where postEncode nwthd = [("type", showableToBytestring_ (nwthd ^. Types.withdrawalType)), ("amount", realToDecimalByteString_ (nwthd ^. Types.amount))] ++ ifJustText "beneficiary_id" (nwthd ^. Types.beneficiaryId) -------------------------------------- BitcoinSendRequest type ------------------------------------- instance POSTEncodeable Types.BitcoinSendRequest where postEncode oreq = [("amount", realToDecimalByteString_ (oreq ^. Types.amount)), ("currency", showableToBytestring_ (oreq ^. Types.currency)), ("address", Txt.encodeUtf8 (oreq ^. Types.address))] ++ ifJustText "description" (oreq ^. Types.description) ++ ifJustText "message" (oreq ^. Types.message) ----------------------------------------- QuoteRequest type ---------------------------------------- instance POSTEncodeable Types.QuoteRequest where postEncode oreq = [("type", showableToBytestring_ (oreq ^. Types.quoteType)), ("pair", showableToBytestring_ (oreq ^. Types.pair)), ("base_amount", realToDecimalByteString_ (oreq ^. Types.baseAmount))] ------------------------------------------ OrderQuote type ----------------------------------------- data OrderQuote_ = OrderQuote_ { orderQuote'id :: Text , orderQuote'type :: Types.QuoteType , orderQuote'pair :: Types.CcyPair , orderQuote'base_amount :: QuotedScientific , orderQuote'counter_amount :: QuotedScientific , orderQuote'created_at :: TimestampMS , orderQuote'expires_at :: TimestampMS , orderQuote'discarded :: Bool , orderQuote'exercised :: Bool } $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''OrderQuote_) instance BitXAesRecordConvert Types.OrderQuote where type Aes Types.OrderQuote = OrderQuote_ aesToRec OrderQuote_ {..} = Types.OrderQuote {orderQuoteId = orderQuote'id, orderQuoteQuoteType = orderQuote'type, orderQuotePair = orderQuote'pair, orderQuoteBaseAmount = qsToScientific orderQuote'base_amount, orderQuoteCounterAmount = qsToScientific orderQuote'counter_amount, orderQuoteCreatedAt = tsmsToUTCTime orderQuote'created_at, orderQuoteExpiresAt = tsmsToUTCTime orderQuote'expires_at, orderQuoteDiscarded = orderQuote'discarded, orderQuoteExercised = orderQuote'exercised} -------------------------------------------- BitXAuth type ----------------------------------------- data BitXAuth_ = BitXAuth_ { bitXAuth'api_key_id :: Text , bitXAuth'api_key_secret :: Text } $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''BitXAuth_) instance BitXAesRecordConvert Types.BitXAuth where type Aes Types.BitXAuth = BitXAuth_ aesToRec BitXAuth_ {..} = Types.BitXAuth {bitXAuthId = bitXAuth'api_key_id, bitXAuthSecret = bitXAuth'api_key_secret} ------------------------------------------ Transaction type ---------------------------------------- data Transaction_ = Transaction_ { transaction'row_index :: Int , transaction'timestamp :: TimestampMS , transaction'balance :: QuotedScientific , transaction'available :: QuotedScientific , transaction'balance_delta :: QuotedScientific , transaction'available_delta :: QuotedScientific , transaction'currency :: Types.Asset , transaction'description :: Text } $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''Transaction_) instance BitXAesRecordConvert Types.Transaction where type Aes Types.Transaction = Transaction_ aesToRec Transaction_ {..} = Types.Transaction {transactionRowIndex = transaction'row_index, transactionTimestamp = tsmsToUTCTime transaction'timestamp, transactionBalance = qsToScientific transaction'balance, transactionAvailable = qsToScientific transaction'available, transactionBalanceDelta = qsToScientific transaction'balance_delta, transactionAvailableDelta = qsToScientific transaction'available_delta, transactionCurrency = transaction'currency, transactionDescription = transaction'description} ---------------------------------------- Transactions type ----------------------------------------- data Transactions_ = Transactions_ { transactions'transactions :: [Transaction_] } $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''Transactions_) instance BitXAesRecordConvert [Types.Transaction] where type Aes [Types.Transaction] = Transactions_ aesToRec Transactions_ {..} = map aesToRec transactions'transactions data PendingTransactions_ = PendingTransactions_ { transactions'pending :: [Transaction_] } $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''PendingTransactions_) instance BitXAesRecordConvert PendingTransactions__ where type Aes PendingTransactions__ = PendingTransactions_ aesToRec PendingTransactions_ {..} = PendingTransactions__ {pendingTransactions__transactions = map aesToRec transactions'pending} data PendingTransactions__ = PendingTransactions__ {pendingTransactions__transactions :: [Types.Transaction]} pendingTransactionsToTransactions :: PendingTransactions__ -> [Types.Transaction] pendingTransactionsToTransactions (PendingTransactions__ tx) = tx -------------------------------------------- Account type ------------------------------------------ data Account_ = Account_ { account'id :: Text , account'name :: Text , account'currency :: Types.Asset } $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''Account_) instance BitXAesRecordConvert Types.Account where type Aes Types.Account = Account_ aesToRec Account_ {..} = Types.Account {accountId = account'id, accountName = account'name, accountCurrency = account'currency} instance POSTEncodeable Types.Account where postEncode acc = [("name", showableToBytestring_ (acc ^. Types.name)), ("currency", showableToBytestring_ (acc ^. Types.currency))] ------------------------------------- MarketOrderRequest type ------------------------------------ instance POSTEncodeable Types.MarketOrderRequest where postEncode moreq = [("type", if (moreq ^. Types.orderType) == Types.BID then "BUY" else "SELL"), ("pair", showableToBytestring_ (moreq ^. Types.pair)), (if (moreq ^. Types.orderType) == Types.BID then "counter_volume" else "base_volume", realToDecimalByteString_ (moreq ^. Types.volume))] ------------------------------------------ PrivateTrade type --------------------------------------- data PrivateTrade_ = PrivateTrade_ { privateTrade'base :: QuotedScientific , privateTrade'counter :: QuotedScientific , privateTrade'fee_base :: QuotedScientific , privateTrade'fee_counter :: QuotedScientific , privateTrade'is_buy :: Bool , privateTrade'order_id :: Types.OrderID , privateTrade'pair :: Types.CcyPair , privateTrade'price :: QuotedInt , privateTrade'timestamp :: TimestampMS , privateTrade'type :: OrderType_ , privateTrade'volume :: QuotedScientific } $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''PrivateTrade_) instance BitXAesRecordConvert Types.PrivateTrade where type Aes Types.PrivateTrade = PrivateTrade_ aesToRec PrivateTrade_ {..} = Types.PrivateTrade {privateTradeBase = qsToScientific privateTrade'base, privateTradeCounter = qsToScientific privateTrade'counter, privateTradeFeeBase = qsToScientific privateTrade'fee_base, privateTradeFeeCounter = qsToScientific privateTrade'fee_counter, privateTradeIsBuy = privateTrade'is_buy, privateTradeOrderId = privateTrade'order_id, privateTradePair = privateTrade'pair, privateTradePrice = qiToInt privateTrade'price, privateTradeTimestamp = tsmsToUTCTime privateTrade'timestamp, privateTradeOrderType = orderTypeParse privateTrade'type, privateTradeVolume = qsToScientific privateTrade'volume} ----------------------------------------- PrivateTrades type ---------------------------------------- data PrivateTrades_ = PrivateTrades_ { privateTrades'trades :: [PrivateTrade_] } $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''PrivateTrades_) instance BitXAesRecordConvert [Types.PrivateTrade] where type Aes [Types.PrivateTrade] = PrivateTrades_ aesToRec PrivateTrades_ {..} = map aesToRec privateTrades'trades -------------------------------------------- FeeInfo type ------------------------------------------- data FeeInfo_ = FeeInfo_ { feeInfo'maker_fee :: QuotedScientific , feeInfo'taker_fee :: QuotedScientific , feeInfo'thirty_day_volume :: QuotedScientific } $(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"} ''FeeInfo_) instance BitXAesRecordConvert Types.FeeInfo where type Aes Types.FeeInfo = FeeInfo_ aesToRec FeeInfo_ {..} = Types.FeeInfo {feeInfoMakerFee = qsToScientific feeInfo'maker_fee, feeInfoTakerFee = qsToScientific feeInfo'taker_fee, feeInfoThirtyDayVolume = qsToScientific feeInfo'thirty_day_volume}