packages feed

bittrex (empty) → 0.1.0.0

raw patch · 9 files changed

+797/−0 lines, 9 filesdep +SHAdep +aesondep +basesetup-changed

Dependencies added: SHA, aeson, base, bytestring, http-client-tls, lens, lens-aeson, scientific, split, text, time, wreq

Files

+ ChangeLog.md view
@@ -0,0 +1,5 @@+# Revision history for bittrex++## 0.1.0.0  -- YYYY-mm-dd++* First version. Released on an unsuspecting world.
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2017, David Johnson++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of David Johnson nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ bittrex.cabal view
@@ -0,0 +1,39 @@+name:                bittrex+version:             0.1.0.0+synopsis:            API bindings to bittrex.com+description:         Haskell bindings to the Bittrex exchange+homepage:            https://github.com/dmjio/bittrex+license:             BSD3+license-file:        LICENSE+author:              David Johnson+maintainer:          djohnson.m@gmail.com+copyright:           David Johnson (c) 2017+category:            Web+build-type:          Simple+extra-source-files:  ChangeLog.md+cabal-version:       >=1.10++library+  exposed-modules:     Bittrex+                       Bittrex.API+                       Bittrex.Types+                       Bittrex.Util+  other-modules:       Bittrex.Internal+  hs-source-dirs:      src+  default-language:    Haskell2010+  build-depends:       aeson+                     , base < 5+                     , bytestring+                     , http-client-tls+                     , lens+                     , lens-aeson+                     , scientific+                     , SHA+                     , split+                     , text+                     , time+                     , wreq++source-repository head+  type:     git+  location: git://github.com/dmjio/bittrex.git
+ src/Bittrex.hs view
@@ -0,0 +1,9 @@+module Bittrex+  ( module Bittrex.API+  , module Bittrex.Types+  , module Bittrex.Util+  ) where++import Bittrex.API+import Bittrex.Types+import Bittrex.Util
+ src/Bittrex/API.hs view
@@ -0,0 +1,296 @@+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE OverloadedStrings   #-}+{-# LANGUAGE RecordWildCards     #-}+{-# LANGUAGE DeriveGeneric       #-}+module Bittrex.API+  (+    -- * Overview+    -- $intro++    -- * Public+    getMarkets+  , getCurrencies+  , getTicker+  , getMarketSummaries+  , getMarketSummary+  , getOrderBook+  , getMarketHistory+    -- * Market+  , buyLimit+  , sellLimit+  , cancel+  , getOpenOrders+    -- * Account+  , getBalances+  , getBalance+  , getDepositAddress+  , withdraw+  , getOrder+  , getOrderHistory+  , getWithdrawalHistory+  , getDepositHistory+  ) where++import           Data.Aeson+import           Data.Monoid+import           Data.Text        (Text)+import qualified Data.Text        as T+import           Data.Scientific++import           Bittrex.Types+import           Bittrex.Util+import           Bittrex.Internal++-- $intro+-- Bittrex provides a simple and powerful REST API to allow you to programatically perform nearly all actions you can from our web interface.+--+-- All requests use the application/json content type and go over https.+-- The base url is https://bittrex.com/api/{version}/.+--+-- All requests are GET requests and all responses come in a default response object with the result in the result field. Always check the success flag to ensure that your API call succeeded.+--+-- We are currently restricting orders to 500 open orders and 200,000 orders a day. We reserve the right to change these settings as we tune the system. If you are affected by these limits as an active trader, please email support@bittrex.com.+--+-- If you have any questions, feedback or recommendation for API support you can post a question in our support center.+--++-- | Used to get the open and available trading markets at Bittrex along with other meta data.+getMarkets+  :: IO (Either ErrorMessage [Market])+getMarkets = callAPI defOpts { path = "getmarkets" }++-- | Used to get all supported currencies at Bittrex along with other meta data.+getCurrencies+  :: IO (Either ErrorMessage [Currency])+getCurrencies = callAPI defOpts { path = "getcurrencies" }++-- | Used to get the current tick values for a market.+getTicker+  :: MarketName -- ^ a string literal for the market (ex: `BTC-LTC`)+  -> IO (Either ErrorMessage Ticker)+getTicker market =+  callAPI defOpts {+      qParams = [("market", show market)]+    , path = "getticker"+    }++-- | Used to get the last 24 hour summary of all active exchanges+getMarketSummaries+  :: IO (Either ErrorMessage Value)+getMarketSummaries =+  callAPI defOpts { path = "getmarketsummaries" }++-- | Used to get the last 24 hour summary of all active exchanges+getMarketSummary+  :: MarketName -- ^ a string literal for the market (ex: `BTC-LTC`)+  -> IO (Either ErrorMessage Value)+getMarketSummary market =+  callAPI defOpts {+      qParams = [("market", show market)]+    , path = "getmarketsummary"+    }++-- | Used to get retrieve the orderbook for a given market+getOrderBook+  :: MarketName -- ^ a string literal for the market (ex: `BTC-LTC`)+  -> OrderBookType -- ^ buy, sell or both to identify the type of orderbook to return.+  -> IO (Either ErrorMessage OrderBook)+getOrderBook market orderBookType =+  callAPI defOpts {+      path = "getorderbook"+    , qParams = [ ("market", show market)+                , ("type", show orderBookType)+                ]+    }++-- | Used to retrieve the latest trades that have occured for a specific market.+getMarketHistory+  :: MarketName -- ^ string literal for the market (ex: `BTC-LTC`)+  -> IO (Either ErrorMessage [MarketHistory])+getMarketHistory market =+  callAPI defOpts {+      path = "getmarkethistory"+    , qParams = pure ("market", show market)+    }++-- | Get all orders that you currently have opened. A specific market can be requested+getOpenOrders+  :: APIKeys    -- ^ Bittrex API credentials+  -> MarketName -- ^ String literal for the market (ie. BTC-LTC)+  -> IO (Either ErrorMessage [OpenOrder])+getOpenOrders keys market =+  callAPI defOpts {+      path      = "getopenorders"+    , apiType   = MarketAPI+    , qParams   = pure ("market", camelToDash $ show market)+    , keys      = keys+    }++-- | Used to place a buy order in a specific market. Use buylimit to place limit orders. Make sure you have the proper permissions set on your API keys for this call to work+buyLimit+  :: APIKeys    -- ^ Bittrex API credentials+  -> MarketName -- ^ A string literal for the market (ex: BTC-LTC)+  -> Quantity   -- ^ The amount to purchase+  -> Rate       -- ^ The rate at which to place the order.+  -> IO (Either ErrorMessage UUID)+buyLimit keys market quantity rate =+  callAPI defOpts {+      path      = "buylimit"+    , keys      = keys+    , apiType   = MarketAPI+    , qParams   = [ ("market", camelToDash $ show market )+                  , ("quantity", formatScientific Fixed Nothing quantity )+                  , ("rate", formatScientific Fixed Nothing rate )+                  ]+    }++-- | Used to place an sell order in a specific market. Use selllimit to place limit orders.+-- Make sure you have the proper permissions set on your API keys for this call to workn+sellLimit+  :: APIKeys    -- ^ Bittrex API credentials+  -> MarketName -- ^ A string literal for the market (ex: BTC-LTC)+  -> Quantity   -- ^ The amount to purchase+  -> Rate       -- ^ The rate at which to place the order+  -> IO (Either ErrorMessage UUID)+sellLimit keys market quantity rate =+  callAPI defOpts {+      path      = "selllimit"+    , keys      = keys+    , apiType   = MarketAPI+    , qParams   = [ ("market", camelToDash $ show market )+                  , ("quantity", formatScientific Fixed Nothing quantity )+                  , ("rate", formatScientific Fixed Nothing rate )+                  ]+    }++-- | Used to cancel a buy or sell order.+cancel+  :: APIKeys -- ^ Bittrex API credentials+  -> UUID    -- ^ uuid of buy or sell order+  -> IO (Either ErrorMessage (Maybe Text))+cancel keys (UUID uuid) =+  callAPI defOpts {+      path      = "cancel"+    , keys      = keys+    , apiType   = MarketAPI+    , qParams   = [ ("uuid", T.unpack uuid ) ]+    }++-- | Used to retrieve all balances from your account+getBalances+  :: APIKeys -- ^ Bittrex API credentials+  -> IO (Either ErrorMessage [Balance])+getBalances keys =+  callAPI defOpts {+      path    = "getbalances"+    , keys    = keys+    , apiType = AccountAPI+    }++-- | Used to retrieve the balance from your account for a specific currency.+getBalance+  :: APIKeys -- ^ Bittrex API credentials+  -> CurrencyName -- ^ a string literal for the currency (ex: LTC)+  -> IO (Either ErrorMessage Balance)+getBalance keys currency =+  callAPI defOpts {+      path    = "getbalance"+    , keys    = keys+    , apiType = AccountAPI+    , qParams = pure ("currency", T.unpack currency )+    }++-- | Used to retrieve or generate an address for a specific currency.+-- If one does not exist, the call will fail and return ADDRESS_GENERATING until one is available.+getDepositAddress+  :: APIKeys -- ^ Bittrex API credentials+  -> CurrencyName -- ^ a string literal for the currency (ie. BTC)+  -> IO (Either ErrorMessage DepositAddress)+getDepositAddress keys currency =+  callAPI defOpts {+      path    = "getdepositaddress"+    , apiType = AccountAPI+    , keys    = keys+    , qParams = pure ("currency", T.unpack currency )+    }++-- | Used to retrieve your withdrawal history.+getWithdrawalHistory+  :: APIKeys -- ^ Bittrex API credentials+  -> CurrencyName+  -> IO (Either ErrorMessage [WithdrawalHistory])+getWithdrawalHistory keys currency =+  callAPI defOpts {+      path    = "getwithdrawalhistory"+    , apiType = AccountAPI+    , keys    = keys+    , qParams = pure ( "currency"+                     , show currency+                     )+    }++-- | Used to retrieve your order history.+getOrderHistory+  :: APIKeys -- ^ Bittrex API credentials+  -> Maybe MarketName -- ^ a string literal for the market (ie. BTC-LTC). If ommited, will return for all markets+  -> IO (Either ErrorMessage [OrderHistory] )+getOrderHistory keys market =+  callAPI defOpts {+      path    = "getorderhistory"+    , apiType = AccountAPI+    , keys    = keys+    , qParams = [ ("market", camelToDash (show m) )+                | Just m <- pure market+                ]+    }++-- | Used to retrieve a single order by uuid.+getOrder+  :: APIKeys -- ^ Bittrex API credentials+  -> UUID    -- ^ the uuid of the buy or sell order+  -> IO (Either ErrorMessage Order)+getOrder keys (UUID uuid) =+  callAPI defOpts {+      path = "getorder"+    , keys = keys+    , apiType = AccountAPI+    , qParams   = [ ("uuid", T.unpack uuid) ]+    }++-- | Used to withdraw funds from your account. note: please account for txfee.+withdraw+  :: APIKeys         -- ^ Bittrex API credentials+  -> CurrencyName    -- ^ A string literal for the currency (ie. BTC)+  -> Quantity        -- ^ The quantity of coins to withdraw+  -> Address         -- ^ The address where to send the funds.+  -> Maybe PaymentId -- ^ used for CryptoNotes/BitShareX/Nxt optional field (memo/paymentid)+  -> IO (Either ErrorMessage UUID)+withdraw keys currency quantity address payment =+  callAPI defOpts {+      path      = "withdraw"+    , keys      = keys+    , apiType   = AccountAPI+    , qParams   = [ ("currency", T.unpack currency )+                  , ("quantity", formatScientific Fixed Nothing quantity )+                  , ("address", address )+                  ] <> [ ("paymentid", show p )+                       | Just p <- pure payment+                       ]+    }++-- | Used to retrieve your deposit history.+getDepositHistory+  :: APIKeys+  -- ^ Bittrex API credentials+  -> Maybe CurrencyName+  -- ^ A string literal for the currecy (ie. BTC). If `Nothing`, will return for all currencies+  -> IO (Either ErrorMessage [DepositHistory])+getDepositHistory keys currency =+  callAPI defOpts {+      path = "getdeposithistory"+    , apiType = AccountAPI+    , keys = keys+    , qParams   = [ ("currency", show c)+                  | Just c <- pure currency+                  ]+    }
+ src/Bittrex/Internal.hs view
@@ -0,0 +1,67 @@+{-# LANGUAGE RecordWildCards   #-}+{-# LANGUAGE OverloadedStrings #-}+module Bittrex.Internal where++import           Control.Lens+import           Data.Aeson+import           Data.Aeson.Lens         (key, nth)+import qualified Data.ByteString.Char8   as BC+import qualified Data.ByteString.Lazy    as L+import           Data.Char+import           Data.Digest.Pure.SHA+import           Data.List+import           Data.List.Split         (splitOn)+import           Data.Monoid+import           Data.Time.Clock.POSIX+import           Network.Wreq+import           Debug.Trace+import           Bittrex.Types++-- | Default API options+defOpts :: APIOpts+defOpts = APIOpts PublicAPI [] "v1.1" mempty (APIKeys mempty mempty)++-- | Internal, used for dispatching an API call+callAPI+  :: FromJSON v+  => APIOpts+  -> IO (Either ErrorMessage v)+callAPI APIOpts {..} = do+  let APIKeys {..} = keys+  nonce <- head . splitOn "." . show <$> getPOSIXTime+  let addAuth = apiType `elem` [AccountAPI, MarketAPI]+      authParams = concat+        [ [ ("apikey", apiKey) | addAuth ]+        , [ ("nonce", nonce)   | addAuth ]+        ]+      addHeader o =+          if addAuth+            then o & header "apisign" .~ [+               BC.pack $ showDigest $+                 hmacSha512 (L.fromStrict (BC.pack secretKey)) $+                   L.fromStrict (BC.pack urlForHash)+               ]+            else o+      urlForHash = init $+        mconcat [ url+                , "?"+                , go =<< do qParams ++ authParams+                ]+      go (k,v) = k <> "=" <> v <> "&"+      url = intercalate "/" [ "https://bittrex.com/api"+                            , version+                            , toLower <$> show apiType+                            , path+                            ]+  r <- getWith (addHeader defaults) urlForHash+  let Just (Bool success) = r ^? responseBody . key "success"+      Just result = r ^? responseBody . key "result"+      Just msg = r ^? responseBody . key "message"+  pure $ if success+           then case fromJSON result of+                  Error s  -> Left (DecodeFailure s result)+                  Success m -> Right m+           else case fromJSON msg of+                  Success m -> Left (BittrexError m)+                  Error s -> Left (DecodeFailure s msg)+
+ src/Bittrex/Types.hs view
@@ -0,0 +1,343 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE DeriveGeneric     #-}+module Bittrex.Types where++import           Data.Aeson+import           Data.Aeson.Types+import           Data.ByteString      (ByteString)+import qualified Data.ByteString      as B+import qualified Data.ByteString.Lazy as L+import           Data.Scientific+import           Data.Text            (Text)+import qualified Data.Text            as T+import           Data.Time+import           GHC.Generics++type Params = [(String,String)]+type Rate = Scientific++data APIType+  = PublicAPI+  | AccountAPI+  | MarketAPI+  deriving (Eq)++instance Show APIType where+  show AccountAPI = "account"+  show PublicAPI  = "public"+  show MarketAPI  = "market"++data APIOpts+  = APIOpts+  { apiType :: APIType+  , qParams :: Params+  , version :: String+  , path    :: String+  , keys    :: APIKeys+  } deriving (Show, Eq)++data ErrorMessage+  = BittrexError BittrexError+  | DecodeFailure String Value+  deriving (Show, Eq, Generic)++data BittrexError+  = INVALID_MARKET+  | MARKET_NOT_PROVIDED+  | APIKEY_NOT_PROVIDED+  | APIKEY_INVALID+  | INVALID_SIGNATURE+  | NONCE_NOT_PROVIDED+  | INVALID_PERMISSION+  | INVALID_CURRENCY+  | WITHDRAWAL_TOO_SMALL+  | CURRENCY_DOES_NOT_EXIST+  deriving (Show, Eq, Generic)++instance FromJSON ErrorMessage+instance FromJSON BittrexError++data MarketName+  = BTC_ADA+  | BTC_LTC+  deriving (Show)++data Ticker = Ticker+  { bid :: Double+  , ask :: Double+  , last :: Double+  } deriving (Generic, Show)++instance FromJSON Ticker where+  parseJSON = withObject "Ticker" $ \o ->+    Ticker <$> o .: "Bid"+           <*> o .: "Ask"+           <*> o .: "Last"++data Market+  = Market+  { marketCurrency :: Text+  , baseCurrency :: Text+  , marketCurrencyLong :: Text+  , baseCurrencyLong :: Text+  , minTradeSize :: Double+  , marketName :: Text+  , isActive :: Bool+  , created :: Text+  } deriving (Show, Eq)++instance FromJSON Market where+  parseJSON = withObject "Market" $ \o ->+    Market <$> o .: "MarketCurrency"+           <*> o .: "BaseCurrency"+           <*> o .: "MarketCurrencyLong"+           <*> o .: "BaseCurrencyLong"+           <*> o .: "MinTradeSize"+           <*> o .: "MarketName"+           <*> o .: "IsActive"+           <*> o .: "Created"++data Currency+  = Currency+  { currency :: Text+  , currencyLong :: Text+  , minConfirmation :: Int+  , txFee :: Double+  , currencyIsActive :: Bool+  , coinType :: Text+  , baseAddress :: Maybe Text+  } deriving (Show, Eq)++instance FromJSON Currency where+  parseJSON = withObject "Currency" $ \o ->+    Currency+      <$> o .: "Currency"+      <*> o .: "CurrencyLong"+      <*> o .: "MinConfirmation"+      <*> o .: "TxFee"+      <*> o .: "IsActive"+      <*> o .: "CoinType"+      <*> o .: "BaseAddress"++data OrderBookType+  = Buy+  | Sell+  | Both+  deriving (Show, Eq)++data OrderBookEntry+  = OrderBookEntry+  { quantity :: Double+  , rate :: Double+  } deriving (Show, Eq)++instance FromJSON OrderBook where+  parseJSON = withObject "OrderBook" $ \o ->+    OrderBook <$> o .: "buy"+              <*> o .: "sell"++data OrderBook+  = OrderBook+  { buy :: [OrderBookEntry]+  , sell :: [OrderBookEntry]+  } deriving (Show, Eq)++instance FromJSON OrderBookEntry where+  parseJSON = withObject "OrderBookEntry" $ \o ->+    OrderBookEntry <$> o .: "Quantity"+                   <*> o .: "Rate"++k :: ByteString+k = "{\"buy\":[{\"Quantity\":12.37000000,\"Rate\":0.02525000}],\"sell\":[{\"Quantity\":32.55412402,\"Rate\":0.02540000}]}"++Right m = eitherDecode (L.fromStrict k) :: Either String OrderBook++data MarketHistory+  = MarketHistory+  { marketHistoryId :: Integer+  , marketHistoryTimeStamp :: Text+  , marketHistoryQuantity :: Double+  , marketHistoryPrice :: Double+  , marketHistoryTotal :: Double+  , marketHistoryFillType :: Text+  , marketHistoryOrderType :: Text+  } deriving (Show, Eq)++instance FromJSON MarketHistory where+  parseJSON = withObject "MarketHistory" $ \o ->+    MarketHistory <$> o .: "Id"+                  <*> o .: "TimeStamp"+                  <*> o .: "Quantity"+                  <*> o .: "Price"+                  <*> o .: "Total"+                  <*> o .: "FillType"+                  <*> o .: "OrderType"++type Quantity = Scientific+type Address = String+type PaymentId = String++-- | API Keys+data APIKeys = APIKeys+  { apiKey :: String+  , secretKey :: String+  } deriving (Show, Eq)++data WithdrawalHistory+  = WithdrawalHistory+  { whPaymentUuid :: Text+  , whCurrency :: Text+  , whAmount :: Scientific+  , whAddress :: Text+  , whOpened :: Text+  , whAuthorized :: Bool+  , whPendingPayment :: Bool+  , whTxCost :: Scientific+  , whTxId :: Text+  , whCanceled :: Bool+  , whInvalidAddress :: Bool+  } deriving (Show, Eq, Generic)++instance FromJSON WithdrawalHistory where+  parseJSON = genericParseJSON defaultOptions {+    fieldLabelModifier = drop 2+  }++data DepositHistory+  = DepositHistory+  { dhCurrency :: Text+  , dhAmount :: Scientific+  , dhLastUpdated :: Text+  , dhConfirmations :: Scientific+  , dhId :: Scientific+  , dhTxId :: Text+  , dhCryptoAddress :: Text+  } deriving (Show, Eq, Generic)++instance FromJSON DepositHistory where+  parseJSON = genericParseJSON defaultOptions {+    fieldLabelModifier = drop 2+  }++type CurrencyName = Text++data DepositAddress+  = DepositAddress+  { daCurrency :: Text+  , daAddress :: Text+  } deriving (Show, Eq, Generic)++instance FromJSON DepositAddress where+  parseJSON = genericParseJSON defaultOptions {+    fieldLabelModifier = drop 2+  }++newtype UUID = UUID Text+  deriving (Show, Eq)++instance FromJSON UUID where+  parseJSON = withObject "UUID" $ \o ->+    UUID <$> o .: "uuid"++data Balance+  = Balance+  { bCurrency :: Text+  , bBalance :: Scientific+  , bAvailable :: Scientific+  , bPending :: Scientific+  , bCryptoAddress :: Text+  , bUuid :: Maybe Text+  } deriving (Show, Eq, Generic)++instance FromJSON Balance where+  parseJSON = genericParseJSON defaultOptions {+    fieldLabelModifier = drop 1+  }++data OrderType+  = SELL+  | BUY+  | LIMIT_SELL+  | LIMIT_BUY+  deriving (Show, Generic, Eq)++instance FromJSON OrderType++data OpenOrder+  = OpenOrder+  { ooUuid :: Maybe Text+  , ooOrderUuid :: Text+  , ooExchange :: Text+  , ooOrderType :: OrderType+  , ooQuantity :: Scientific+  , ooQuantityRemaining :: Scientific+  , ooLimit :: Scientific+  , ooCommissionPaid :: Scientific+  , ooPrice :: Scientific+  , ooPricePerUnit :: Maybe Scientific+  , ooOpened :: Text+  , ooClosed :: Maybe Text+  , ooCancelInitiated :: Bool+  , ooImmediateOrCancel :: Bool+  , ooIsConditional :: Bool+  , ooCondition :: Maybe Text+  , ooConditionTarget :: Maybe Text+  } deriving (Show, Eq, Generic)++instance FromJSON OpenOrder where+  parseJSON = genericParseJSON defaultOptions {+    fieldLabelModifier = drop 2+  }++data OrderHistory+  = OrderHistory+    { ohOrderUuid :: Text+    , ohExchange :: Text+    , ohTimeStamp :: Text+    , ohOrderType :: OrderType+    , ohLimit :: Scientific+    , ohQuantity :: Scientific+    , ohQuantityRemaining :: Scientific+    , ohCommission :: Scientific+    , ohPrice :: Scientific+    , ohPricePerUnit :: Maybe Scientific+    , ohIsConditional :: Bool+    , ohImmediateOrCancel :: Bool+    } deriving (Show, Eq, Generic)++instance FromJSON OrderHistory where+  parseJSON = genericParseJSON defaultOptions {+    fieldLabelModifier = drop 2+  }++data Order+  = Order+    { oAccountId :: Maybe Text+    , oOrderUuid :: Text+    , oExchange :: Text+    , oOrderType :: OrderType+    , oQuantity :: Scientific+    , oQuantityRemaining :: Scientific+    , oLimit :: Scientific+    , oReserved :: Scientific+    , oReservedRemaining :: Scientific+    , oCommissionReserved :: Scientific+    , oCommissionReserveRemaining :: Scientific+    , oCommissionPaid :: Scientific+    , oPrice :: Scientific+    , oPricePerUnit :: Maybe Scientific+    , oOpen :: Text+    , oIsOpen :: Bool+    , oSentinal :: Text+    , oTimeStamp :: Text+    , oCommission :: Scientific+    , oIsConditional :: Bool+    , oImmediateOrCancel :: Bool+    , oCancelInitiated :: Bool+    , oCondition :: Text+    } deriving (Show, Eq, Generic)++instance FromJSON Order where+  parseJSON = genericParseJSON defaultOptions {+    fieldLabelModifier = drop 1+  }
+ src/Bittrex/Util.hs view
@@ -0,0 +1,6 @@+module Bittrex.Util ( camelToDash ) where++camelToDash :: String -> String+camelToDash [] = []+camelToDash ('_':xs) = '-':camelToDash xs+camelToDash (x:xs) = x:camelToDash xs