coinbase-pro 0.6.0.0 → 0.7.0.0
raw patch · 12 files changed
+331/−108 lines, 12 files
Files
- README.md +67/−45
- changelog.md +8/−0
- coinbase-pro.cabal +2/−2
- src/example/request/Main.hs +27/−20
- src/lib/CoinbasePro/Authenticated.hs +21/−2
- src/lib/CoinbasePro/Authenticated/API.hs +9/−2
- src/lib/CoinbasePro/Authenticated/Accounts.hs +43/−4
- src/lib/CoinbasePro/Headers.hs +1/−1
- src/lib/CoinbasePro/Request.hs +70/−9
- src/lib/CoinbasePro/Types.hs +49/−2
- src/lib/CoinbasePro/Unauthenticated.hs +26/−19
- src/lib/CoinbasePro/Unauthenticated/API.hs +8/−2
README.md view
@@ -1,50 +1,83 @@ # coinbase-pro -## Request API+Client for Coinabse Pro REST and Websocket APIS. -```haskell-{-# LANGUAGE OverloadedStrings #-}+Here is a list of implemented/unimplemented features: -module Main where+- Market Data+ - [x] Products+ - [x] Get Products+ - [x] Get Product Order Book+ - [x] Get Product Ticker+ - [x] Get Trades+ - [x] Get Historic Rates+ - [x] Get 24hr Stats+ - [x] Currencies+ - [x] Get Currencies+ - [x] Time+- Private+ - [ ] Accounts+ - [x] List Accounts+ - [x] Get An Account+ - [ ] Get Account History+ - [ ] Get Holds+ - [ ] Orders+ - [x] Place a New Order+ - [x] Cancel an Order+ - [x] Cancel all+ - [x] List Orders+ - [ ] Get an Order+ - [x] Fills+ - [x] List Fills+ - [ ] Deposits+ - [ ] Payment Method+ - [ ] Coinbase+ - [ ] Withdrawals+ - [ ] Payment Method+ - [ ] Coinbase+ - [ ] Crypto+ - [ ] Stablecoin Conversions+ - [ ] Create Conversion+ - [ ] Payment Methods+ - [ ] List Payment Methods+ - [x] Fees+ - [x] Get Current Fees+ - [ ] Reports+ - [ ] Create a new report+ - [ ] Get report status+ - [x] User Account+ - [x] Trailing Volume+- Websocket Feed+ - [x] Channels+ - [x] The heartbeat channel+ - [ ] The status channel+ - [x] The ticker channel+ - [x] The level2 channel+ - [ ] The user channel+ - [x] The matches channel+ - [x] The full channel+- FIX API+ - [ ] Messages -import Control.Monad.IO.Class (liftIO) -import CoinbasePro.Authenticated-import CoinbasePro.Authenticated.Accounts-import CoinbasePro.Authenticated.Orders-import CoinbasePro.Headers-import CoinbasePro.MarketData.Types hiding (time)-import CoinbasePro.Request-import CoinbasePro.Types hiding (time)-import CoinbasePro.Unauthenticated+## Request API +### Market Data Requests -main :: IO ()-main = do- stats btcusd >>= print- candles btcusd Nothing Nothing Minute >>= print- trades btcusd >>= print- time >>= print- products >>= print- aggregateOrderBook btcusd (Just Best) >>= print- aggregateOrderBook btcusd (Just TopFifty) >>= print- fullOrderBook btcusd >>= print- runCbAuthT cpc $ do- accounts >>= liftIO . print- account accountId >>= liftIO . print- fills (Just btcusd) Nothing >>= liftIO . print- listOrders (Just [All]) (Just btcusd) >>= liftIO . print- placeOrder btcusd Sell (Size 0.001) (Price 99999.00) True Nothing Nothing Nothing >>= liftIO . print- placeOrder btcusd Buy (Size 1.0) (Price 1.00) True Nothing Nothing Nothing >>= liftIO . print- cancelOrder (OrderId "<cancel-order-id>")- cancelAll (Just btcusd) >>= liftIO . print+```haskell+run (trades (ProductId "BTC-USD")) >>= print+```++### Authenticated Private Requests++```haskell+runCbAuthT cpc $ do+ fills (Just btcusd) Nothing >>= liftIO . print where accessKey = CBAccessKey "<access-key>" secretKey = CBSecretKey "<secret-key>" passphrase = CBAccessPassphrase "<passphrase>" cpc = CoinbaseProCredentials accessKey secretKey passphrase- accountId = AccountId "<account-id>"- btcusd = ProductId "BTC-USD" ``` ## Websocket API@@ -53,17 +86,6 @@ ```haskell-{-# LANGUAGE OverloadedStrings #-}--module Main where--import Control.Monad (forever)-import qualified System.IO.Streams as Streams--import CoinbasePro.Types (ProductId (..))-import CoinbasePro.WebSocketFeed (subscribeToFeed)-import CoinbasePro.WebSocketFeed.Request (ChannelName (..))- main :: IO () main = do msgs <- subscribeToFeed [ProductId "BTC-USD"] [Ticker]
+ changelog.md view
@@ -0,0 +1,8 @@+# Version 0.7.0.0+ - Unauthenticated requests are now all in the `ClientM` monad. `CoinbasePro.Request.run` is now required+ to operate in the IO monad.++ Example: `run (trades (ProductId "BTC-USD")) >>= print`++- Added `run_`, `runSandbox`, `runSandboxWithManager` in `CoinbasePro.Request`+- Added `currencies`, `fees`, and `trailingVolume` queries
coinbase-pro.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: bf03411c07a63b43dbdda3129795b4e4da28379155e97759420fa4908122050c+-- hash: 1ab32e4b55d042111e39fa8c604bf0b9bfeae0e5462638e322911b1ae0a0b59a name: coinbase-pro-version: 0.6.0.0+version: 0.7.0.0 synopsis: Client for Coinbase Pro description: Client for Coinbase Pro REST and Websocket APIs category: Web, Finance
src/example/request/Main.hs view
@@ -10,32 +10,39 @@ import CoinbasePro.Authenticated.Orders import CoinbasePro.Authenticated.Request import CoinbasePro.MarketData.Types hiding (time)+import CoinbasePro.Request import CoinbasePro.Types hiding (time) import CoinbasePro.Unauthenticated main :: IO () main = do- stats btcusd >>= print- candles btcusd Nothing Nothing Minute >>= print- trades btcusd >>= print- time >>= print- products >>= print- aggregateOrderBook btcusd (Just Best) >>= print- aggregateOrderBook btcusd (Just TopFifty) >>= print- fullOrderBook btcusd >>= print- runCbAuthT cpc $ do- accounts >>= liftIO . print- account aid >>= liftIO . print- fills (Just btcusd) Nothing >>= liftIO . print- listOrders (Just [All]) (Just btcusd) >>= liftIO . print- placeOrder btcusd Sell (Size 0.001) (Price 99999.00) True Nothing Nothing Nothing >>= liftIO . print- placeOrder btcusd Buy (Size 1.0) (Price 1.00) True Nothing Nothing Nothing >>= liftIO . print- cancelAll (Just btcusd) >>= liftIO . print+ run (stats btcusd) >>= print+ run (stats btcusd) >>= print+ run (candles btcusd Nothing Nothing Minute) >>= print+ run (trades btcusd) >>= print+ run time >>= print+ run products >>= print+ run (aggregateOrderBook btcusd (Just Best)) >>= print+ run (aggregateOrderBook btcusd (Just TopFifty)) >>= print+ run (fullOrderBook btcusd) >>= print+ run_ $ do+ currencies+ products+ -- runCbAuthT cpc $ do+ -- accounts >>= liftIO . print+ -- fees >>= liftIO . print+ -- trailingVolume >>= liftIO . print+ -- account aid >>= liftIO . print+ -- fills (Just btcusd) Nothing >>= liftIO . print+ -- listOrders (Just [All]) (Just btcusd) >>= liftIO . print+ -- placeOrder btcusd Sell (Size 0.001) (Price 99999.00) True Nothing Nothing Nothing >>= liftIO . print+ -- placeOrder btcusd Buy (Size 1.0) (Price 1.00) True Nothing Nothing Nothing >>= liftIO . print+ -- cancelAll (Just btcusd) >>= liftIO . print where- accessKey = CBAccessKey "accesskey"- secretKey = CBSecretKey "secretkey"- passphrase = CBAccessPassphrase "passphrase"+ accessKey = CBAccessKey "315dd0794fd50876e0c3235e79fb7a92"+ secretKey = CBSecretKey "M37/sDmlZtI+Qxm9ODr6RNWzfoW+4w/fyyx5x8LxSRg8keHjt8Aepr+RmlprYzUaOQoi/ZHsYu0bNGiMUDl35g=="+ passphrase = CBAccessPassphrase "test123" cpc = CoinbaseProCredentials accessKey secretKey passphrase- aid = AccountId "accountid"+ accountId = AccountId "e4a37481-12e7-4564-b073-90627d202c2a" btcusd = ProductId "BTC-USD"
src/lib/CoinbasePro/Authenticated.hs view
@@ -6,10 +6,12 @@ ( accounts , account , listOrders- , fills , placeOrder , cancelOrder , cancelAll+ , fills+ , fees+ , trailingVolume ) where @@ -28,7 +30,8 @@ simpleQueryToQuery) import Servant.Client (ClientM) -import CoinbasePro.Authenticated.Accounts (Account, AccountId (..))+import CoinbasePro.Authenticated.Accounts (Account, AccountId (..),+ Fees, TrailingVolume (..)) import qualified CoinbasePro.Authenticated.API as API import CoinbasePro.Authenticated.Fills (Fill) import CoinbasePro.Authenticated.Orders (Order, PlaceOrderBody (..),@@ -108,6 +111,22 @@ mkSimpleQuery :: Maybe ProductId -> Maybe OrderId -> SimpleQuery mkSimpleQuery p o = mkProductQuery p <> mkOrderIdQuery o+++-- | https://docs.pro.coinbase.com/?javascript#get-current-fees+fees :: CBAuthT ClientM Fees+fees = authRequest methodGet mkRequestPath "" API.fees+ where+ mkRequestPath :: RequestPath+ mkRequestPath = "/fees"+++-- | https://docs.pro.coinbase.com/?javascript#trailing-volume+trailingVolume :: CBAuthT ClientM [TrailingVolume]+trailingVolume = authRequest methodGet mkRequestPath "" API.trailingVolume+ where+ mkRequestPath :: RequestPath+ mkRequestPath = "/users/self/trailing-volume" mkSimpleQueryItem :: String -> Text -> SimpleQueryItem
src/lib/CoinbasePro/Authenticated/API.hs view
@@ -10,6 +10,8 @@ , cancelOrder , cancelAll , fills+ , fees+ , trailingVolume ) where import Data.Proxy (Proxy (..))@@ -20,7 +22,8 @@ import Servant.Client import Servant.Client.Core (AuthenticatedRequest) -import CoinbasePro.Authenticated.Accounts (Account, AccountId (..))+import CoinbasePro.Authenticated.Accounts (Account, AccountId (..),+ Fees, TrailingVolume) import CoinbasePro.Authenticated.Fills (Fill) import CoinbasePro.Authenticated.Orders (Order, PlaceOrderBody (..), Status (..))@@ -37,6 +40,8 @@ :<|> "orders" :> Capture "order_id" OrderId :> AuthDelete NoContent :<|> "orders" :> QueryParam "product_id" ProductId :> AuthDelete [OrderId] :<|> "fills" :> QueryParam "product_id" ProductId :> QueryParam "order_id" OrderId :> AuthGet [Fill]+ :<|> "fees" :> AuthGet Fees+ :<|> "users" :> "self" :> "trailing-volume" :> AuthGet [TrailingVolume] api :: Proxy API@@ -50,4 +55,6 @@ cancelOrder :: OrderId -> AuthenticatedRequest (AuthProtect "CBAuth") -> ClientM NoContent cancelAll :: Maybe ProductId -> AuthenticatedRequest (AuthProtect "CBAuth") -> ClientM [OrderId] fills :: Maybe ProductId -> Maybe OrderId -> AuthenticatedRequest (AuthProtect "CBAuth") -> ClientM [Fill]-accounts :<|> singleAccount :<|> listOrders :<|> placeOrder :<|> cancelOrder :<|> cancelAll :<|> fills = client api+fees :: AuthenticatedRequest (AuthProtect "CBAuth") -> ClientM Fees+trailingVolume :: AuthenticatedRequest (AuthProtect "CBAuth") -> ClientM [TrailingVolume]+accounts :<|> singleAccount :<|> listOrders :<|> placeOrder :<|> cancelOrder :<|> cancelAll :<|> fills :<|> fees :<|> trailingVolume = client api
src/lib/CoinbasePro/Authenticated/Accounts.hs view
@@ -1,4 +1,6 @@-{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell #-} module CoinbasePro.Authenticated.Accounts ( Account (..)@@ -6,13 +8,21 @@ , Currency (..) , Balance (..) , ProfileId (..)+ , Fees (..)+ , TrailingVolume (..) ) where -import Data.Aeson (FromJSON (..), withObject, (.:))-import Data.Text (Text)-import Web.HttpApiData (ToHttpApiData (..))+import Data.Aeson (FromJSON (..), ToJSON, withObject, (.:))+import Data.Aeson.Casing (snakeCase)+import Data.Aeson.TH (defaultOptions, deriveJSON,+ fieldLabelModifier)+import Data.Text (Text)+import Data.Time.Clock (UTCTime)+import Web.HttpApiData (ToHttpApiData (..)) +import CoinbasePro.Types (ProductId, Volume (..)) + newtype AccountId = AccountId Text deriving (Eq, Show) @@ -52,3 +62,32 @@ <*> (Balance . read <$> o .: "available") <*> (Balance . read <$> o .: "hold") <*> (ProfileId <$> o .: "profile_id")+++newtype FeeRate = FeeRate { unFeeRate :: Double }+ deriving (Eq, Show, FromJSON, ToJSON)+++data Fees = Fees+ { makerFeeRate :: FeeRate+ , takerFeeRate :: FeeRate+ , usdVolume :: Volume+ } deriving (Eq, Show)+++instance FromJSON Fees where+ parseJSON = withObject "fees" $ \o -> Fees+ <$> (FeeRate . read <$> o .: "maker_fee_rate")+ <*> (FeeRate . read <$> o .: "taker_fee_rate")+ <*> (Volume . read <$> o .: "usd_volume")+++data TrailingVolume = TrailingVolume+ { productId :: ProductId+ , exchangeVolume :: Volume+ , volume :: Volume+ , recordedAt :: UTCTime+ } deriving (Eq, Show)+++deriveJSON defaultOptions { fieldLabelModifier = snakeCase } ''TrailingVolume
src/lib/CoinbasePro/Headers.hs view
@@ -24,7 +24,7 @@ userAgent :: UserAgent-userAgent = UserAgent "coinbase-pro/0.6"+userAgent = UserAgent "coinbase-pro/0.7" type UserAgentHeader = RequiredHeader "User-Agent" UserAgent
src/lib/CoinbasePro/Request.hs view
@@ -10,12 +10,20 @@ , CBGet , CBRequest + -- * Production , run- , apiEndpoint+ , run_+ , runWithManager++ -- * Sandbox+ , runSandbox+ , runSandbox_+ , runSandboxWithManager ) where import Control.Exception (throw)-import Network.HTTP.Client (newManager)+import Control.Monad (void)+import Network.HTTP.Client (Manager, newManager) import Network.HTTP.Client.TLS (tlsManagerSettings) import Servant.API ((:>), Get, JSON) import Servant.Client@@ -33,12 +41,65 @@ type Body = String -apiEndpoint :: String-apiEndpoint = "api.pro.coinbase.com"+------------------------------------------------------------------------------+-- | Runs a coinbase pro HTTPS request and returns the result `a`+--+-- > run products >>= print+--+run :: ClientM a -> IO a+run f = flip runWithManager f =<< newManager tlsManagerSettings -run :: ClientM a -> IO a-run f = do- mgr <- newManager tlsManagerSettings- result <- runClientM f (mkClientEnv mgr (BaseUrl Https apiEndpoint 443 mempty))- either throw return result+------------------------------------------------------------------------------+-- | Same as 'run', except uses `()` instead of a type `a`+run_ :: ClientM a -> IO ()+run_ = void . run+++------------------------------------------------------------------------------+-- | Allows the user to use their own 'Network.HTTP.Client.Types.ManagerSettings`+-- with 'run'+--+-- @+-- do $+-- mgr <- newManager tlsManagerSettings+-- prds <- runWithManager mgr products+-- print prds+-- @+--+runWithManager :: Manager -> ClientM a -> IO a+runWithManager mgr f = either throw return =<<+ runClientM f (mkClientEnv mgr (BaseUrl Https production 443 mempty))+ where+ production = "api.pro.coinbase.com"++------------------------------------------------------------------------------+-- | Runs a coinbase pro HTTPS request in sandbox and returns the result 'a'+--+-- > run products >>= print+--+runSandbox :: ClientM a -> IO a+runSandbox f = flip runSandboxWithManager f =<< newManager tlsManagerSettings+++------------------------------------------------------------------------------+-- | Same as 'runSandbox', except uses '()' instead of a type 'a'+runSandbox_ :: ClientM a -> IO ()+runSandbox_ = void . runSandbox+++------------------------------------------------------------------------------+-- | Allows the user to use their own 'Network.HTTP.Client.Types.ManagerSettings`+--+-- @+-- do $+-- mgr <- newManager tlsManagerSettings+-- prds <- runSandboxWithManager mgr products+-- print prds+-- @+--+runSandboxWithManager :: Manager -> ClientM a -> IO a+runSandboxWithManager mgr f = either throw return =<<+ runClientM f (mkClientEnv mgr (BaseUrl Https sandbox 443 mempty))+ where+ sandbox = "api-public.sandbox.pro.coinbase.com"
src/lib/CoinbasePro/Types.hs view
@@ -10,6 +10,7 @@ , Sequence , Side (..) , Size (..)+ , Volume (..) , TradeId (..) , Funds , OrderType (..)@@ -17,12 +18,14 @@ , Candle (..) , CandleGranularity (..) , TwentyFourHourStats (..)+ , Currency (..) , filterOrderFieldName ) where -import Data.Aeson (FromJSON, ToJSON, parseJSON, toJSON,- withArray, withText)+import Data.Aeson (FromJSON, ToJSON, Value (Null),+ parseJSON, toJSON, withArray,+ withObject, withText, (.:), (.:?)) import qualified Data.Aeson as A import Data.Aeson.Casing (camelCase, snakeCase) import Data.Aeson.TH (constructorTagModifier, defaultOptions,@@ -209,3 +212,47 @@ deriveJSON defaultOptions { fieldLabelModifier = init . init } ''TwentyFourHourStats+++data CurrencyDetails = CurrencyDetails+ { cdType :: Text+ , symbol :: Maybe Text+ , networkConfirmations :: Maybe Int+ , sortOrder :: Maybe Int+ , cryptoAddressLink :: Maybe Text+ , pushPaymentMethods :: [Text]+ , groupTypes :: Maybe [Text]+ , maxPrecision :: Maybe Double+ } deriving (Eq, Show)+++instance FromJSON CurrencyDetails where+ parseJSON = withObject "currency details" $ \o -> CurrencyDetails+ <$> o .: "type"+ <*> o .:? "symbol"+ <*> o .:? "network_confirmations"+ <*> o .:? "set_order"+ <*> o .:? "crypto_address_link"+ <*> o .: "push_payment_methods"+ <*> o .:? "group_types"+ <*> o .:? "max_precision"+++data Currency = Currency+ { id :: Text+ , name :: Text+ , minSize :: Double+ , status :: Text+ , message :: Maybe Text+ , details :: CurrencyDetails+ } deriving (Eq, Show)+++instance FromJSON Currency where+ parseJSON = withObject "currency" $ \o -> Currency+ <$> o .: "id"+ <*> o .: "name"+ <*> (read <$> o .: "min_size")+ <*> o .: "status"+ <*> o .:? "message"+ <*> o .: "details"
src/lib/CoinbasePro/Unauthenticated.hs view
@@ -3,15 +3,17 @@ module CoinbasePro.Unauthenticated ( products- , time , aggregateOrderBook , fullOrderBook , trades , candles , stats+ , currencies+ , time ) where import Data.Time.Clock (UTCTime)+import Servant.Client (ClientM) import CoinbasePro.Headers (userAgent) import CoinbasePro.MarketData.AggregateOrderBook (AggregateOrderBook)@@ -23,41 +25,46 @@ import CoinbasePro.Request (run) import CoinbasePro.Types (Candle, CandleGranularity,- ProductId,+ Currency, ProductId, TwentyFourHourStats) import qualified CoinbasePro.Unauthenticated.API as API -- | https://docs.pro.coinbase.com/#get-products-products :: IO [Product]-products = run $ API.products userAgent----- | https://docs.pro.coinbase.com/#time-time :: IO CBTime-time = run $ API.time userAgent+products :: ClientM [Product]+products = API.products userAgent -- | https://docs.pro.coinbase.com/#get-product-order-book-aggregateOrderBook :: ProductId -> Maybe AggregateBookLevel -> IO AggregateOrderBook-aggregateOrderBook prid agg = run $ API.aggregateOrderBook prid agg userAgent+aggregateOrderBook :: ProductId -> Maybe AggregateBookLevel -> ClientM AggregateOrderBook+aggregateOrderBook prid agg = API.aggregateOrderBook prid agg userAgent -- | https://docs.pro.coinbase.com/#get-product-order-book-fullOrderBook :: ProductId -> IO FullOrderBook-fullOrderBook prid = run $ API.fullOrderBook prid (Just FullBookLevel) userAgent+fullOrderBook :: ProductId -> ClientM FullOrderBook+fullOrderBook prid = API.fullOrderBook prid (Just FullBookLevel) userAgent -- | https://docs.pro.coinbase.com/#get-trades-trades :: ProductId -> IO [Trade]-trades prid = run $ API.trades prid userAgent+trades :: ProductId -> ClientM [Trade]+trades prid = API.trades prid userAgent -- | https://docs.pro.coinbase.com/#get-historic-rates-candles :: ProductId -> Maybe UTCTime -> Maybe UTCTime -> CandleGranularity -> IO [Candle]-candles prid start end cg = run $ API.candles prid start end cg userAgent+candles :: ProductId -> Maybe UTCTime -> Maybe UTCTime -> CandleGranularity -> ClientM [Candle]+candles prid start end cg = API.candles prid start end cg userAgent -- | https://docs.pro.coinbase.com/#get-24hr-stats-stats :: ProductId -> IO TwentyFourHourStats-stats prid = run $ API.stats prid userAgent+stats :: ProductId -> ClientM TwentyFourHourStats+stats prid = API.stats prid userAgent+++-- | https://docs.pro.coinbase.com/?javascript#get-currencies+currencies :: ClientM [Currency]+currencies = API.currencies userAgent+++-- | https://docs.pro.coinbase.com/#time+time :: ClientM CBTime+time = API.time userAgent
src/lib/CoinbasePro/Unauthenticated/API.hs view
@@ -8,6 +8,7 @@ , trades , candles , stats+ , currencies , time ) where @@ -28,7 +29,7 @@ import CoinbasePro.Request (CBGet, CBRequest) import CoinbasePro.Types (Candle, CandleGranularity,- ProductId,+ Currency, ProductId, TwentyFourHourStats) @@ -64,6 +65,9 @@ :> "stats" :> CBGet TwentyFourHourStats +type Currencies = "currencies"+ :> CBGet [Currency]+ type Time = "time" :> CBGet CBTime type API = Products@@ -72,6 +76,7 @@ :<|> Trades :<|> Candles :<|> Stats+ :<|> Currencies :<|> Time @@ -85,5 +90,6 @@ trades :: ProductId -> CBRequest [Trade] candles :: ProductId -> Maybe UTCTime -> Maybe UTCTime -> CandleGranularity -> CBRequest [Candle] stats :: ProductId -> CBRequest TwentyFourHourStats+currencies :: CBRequest [Currency] time :: CBRequest CBTime-products :<|> aggregateOrderBook :<|> fullOrderBook :<|> trades :<|> candles :<|> stats :<|> time = client api+products :<|> aggregateOrderBook :<|> fullOrderBook :<|> trades :<|> candles :<|> stats :<|> currencies :<|> time = client api