{-# LANGUAGE CPP #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-} module Coinbase.Exchange.MarketData ( getProducts , getTopOfBook , getTop50OfBook , getOrderBook , getProductTicker , getTrades , getHistory , getStats , getCurrencies , getExchangeTime , module Coinbase.Exchange.Types.MarketData ) where import Control.Monad.Except import Control.Monad.Reader import Control.Monad.Trans.Resource import Data.List import qualified Data.Text as T import Data.Time import Data.UUID.Aeson () #if MIN_VERSION_time(1,5,0) import Data.Time.Format (defaultTimeLocale) #else import System.Locale (defaultTimeLocale) #endif import Coinbase.Exchange.Rest import Coinbase.Exchange.Types import Coinbase.Exchange.Types.Core import Coinbase.Exchange.Types.MarketData -- Products getProducts :: (MonadResource m, MonadReader ExchangeConf m, MonadError ExchangeFailure m) => m [Product] getProducts = coinbaseGet False "/products" voidBody -- Order Book getTopOfBook :: (MonadResource m, MonadReader ExchangeConf m, MonadError ExchangeFailure m) => ProductId -> m (Book Aggregate) getTopOfBook (ProductId p) = coinbaseGet False ("/products/" ++ T.unpack p ++ "/book?level=1") voidBody getTop50OfBook :: (MonadResource m, MonadReader ExchangeConf m, MonadError ExchangeFailure m) => ProductId -> m (Book Aggregate) getTop50OfBook (ProductId p) = coinbaseGet False ("/products/" ++ T.unpack p ++ "/book?level=2") voidBody getOrderBook :: (MonadResource m, MonadReader ExchangeConf m, MonadError ExchangeFailure m) => ProductId -> m (Book OrderId) getOrderBook (ProductId p) = coinbaseGet False ("/products/" ++ T.unpack p ++ "/book?level=3") voidBody -- Product Ticker getProductTicker :: (MonadResource m, MonadReader ExchangeConf m, MonadError ExchangeFailure m) => ProductId -> m Tick getProductTicker (ProductId p) = coinbaseGet False ("/products/" ++ T.unpack p ++ "/ticker") voidBody -- Product Trades -- | Currently Broken: coinbase api doesn't return valid ISO 8601 dates for this route. getTrades :: (MonadResource m, MonadReader ExchangeConf m, MonadError ExchangeFailure m) => ProductId -> m [Trade] getTrades (ProductId p) = coinbaseGet False ("/products/" ++ T.unpack p ++ "/trades") voidBody -- Historic Rates (Candles) type StartTime = UTCTime type EndTime = UTCTime type Scale = Int getHistory :: (MonadResource m, MonadReader ExchangeConf m, MonadError ExchangeFailure m) => ProductId -> Maybe StartTime -> Maybe EndTime -> Maybe Scale -> m [Candle] getHistory (ProductId p) start end scale = coinbaseGet False path voidBody where path = "/products/" ++ T.unpack p ++ "/candles?" ++ params params = intercalate "&" $ map (\(k, v) -> k ++ "=" ++ v) $ start' ++ end' ++ scale' start' = case start of Nothing -> [] Just t -> [( "start", fmt t)] end' = case end of Nothing -> [] Just t -> [( "end", fmt t)] scale' = case scale of Nothing -> [] Just s -> [("granularity", show s)] fmt t = let t' = formatTime defaultTimeLocale "%FT%T." t in t' ++ take 6 (formatTime defaultTimeLocale "%q" t) ++ "Z" -- Product Stats getStats :: (MonadResource m, MonadReader ExchangeConf m, MonadError ExchangeFailure m) => ProductId -> m Stats getStats (ProductId p) = coinbaseGet False ("/products/" ++ T.unpack p ++ "/stats") voidBody -- Exchange Currencies getCurrencies :: (MonadResource m, MonadReader ExchangeConf m, MonadError ExchangeFailure m) => m [Currency] getCurrencies = coinbaseGet False "/currencies" voidBody -- Exchange Time getExchangeTime :: (MonadResource m, MonadReader ExchangeConf m, MonadError ExchangeFailure m) => m ExchangeTime getExchangeTime = coinbaseGet False "/time" voidBody