coinbase-pro-0.9.3.1: src/lib/CoinbasePro/Authenticated.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeFamilies #-}
module CoinbasePro.Authenticated
( accounts
, account
, accountHistory
, accountHolds
, listOrders
, getOrder
, getClientOrder
, placeOrder
, placeOrderBody
, cancelOrder
, cancelAll
, fills
, fees
, trailingVolume
, limits
, deposits
, withdrawals
, transfer
, makeDeposit
, makeCoinbaseDeposit
, cryptoDepositAddress
, makeWithdrawal
, makeCoinbaseWithdrawal
, makeCryptoWithdrawal
, withdrawalFeeEstimate
, createStablecoinConversion
, paymentMethods
, coinbaseAccounts
, profiles
, profile
, profileTransfer
, createReport
, getReport
, getOracle
) where
import Control.Monad (void)
import Data.Aeson (ToJSON, encode)
import Data.ByteString (ByteString)
import qualified Data.ByteString.Lazy.Char8 as LC8
import Data.Maybe (fromMaybe)
import qualified Data.Set as S
import Data.Text (Text, pack)
import Data.Text.Encoding (encodeUtf8)
import Data.Time.Clock (UTCTime)
import Data.UUID (toText)
import Network.HTTP.Types (SimpleQuery,
SimpleQueryItem,
methodDelete,
methodGet,
methodPost,
renderQuery,
simpleQueryToQuery)
import Servant.Client (ClientM)
import qualified CoinbasePro.Authenticated.API as API
import CoinbasePro.Authenticated.Accounts (Account,
AccountHistory,
AccountId (..),
Fees, Hold,
TrailingVolume (..))
import CoinbasePro.Authenticated.CoinbaseAccounts (CoinbaseAccount)
import CoinbasePro.Authenticated.Conversion (StablecoinConversionRequest (..),
StablecoinConversionResponse)
import CoinbasePro.Authenticated.Deposit (CoinbaseDepositRequest (..),
CryptoDepositAddress,
DepositRequest (..),
DepositResponse)
import CoinbasePro.Authenticated.Fills (Fill)
import CoinbasePro.Authenticated.Limits (Limits)
import CoinbasePro.Authenticated.Oracle (OracleResponse)
import CoinbasePro.Authenticated.Orders (Order,
PlaceOrderBody (..),
STP, Status (..),
Statuses (..),
TimeInForce,
statuses)
import CoinbasePro.Authenticated.Payment (PaymentMethod,
PaymentMethodId (..))
import CoinbasePro.Authenticated.Profile (Profile,
ProfileTransfer (..))
import CoinbasePro.Authenticated.Report (ReportId (..),
ReportRequest (..),
ReportResponse)
import CoinbasePro.Authenticated.Request (CBAuthT (..),
authRequest)
import CoinbasePro.Authenticated.Transfer (Transfer,
TransferType (..))
import CoinbasePro.Authenticated.Withdrawal (CoinbaseWithdrawalRequest (..),
CryptoWithdrawalRequest (..),
CryptoWithdrawalResponse,
WithdrawalFeeEstimateResponse (..),
WithdrawalRequest (..),
WithdrawalResponse)
import CoinbasePro.Request (emptyBody,
encodeRequestPath)
import CoinbasePro.Types (ClientOrderId (..),
CryptoAddress (..),
CurrencyType (..),
OrderId (..),
OrderType, Price,
ProductId (..),
ProfileId, Side,
Size)
accountsPath :: Text
accountsPath = "accounts"
ordersPath :: Text
ordersPath = "orders"
depositsPath :: Text
depositsPath = "deposits"
withdrawalsPath :: Text
withdrawalsPath = "withdrawals"
coinbaseAccountPath :: Text
coinbaseAccountPath = "coinbase-account"
profilesPath :: Text
profilesPath = "profiles"
transferPath :: Text
transferPath = "transfer"
usersPath :: Text
usersPath = "users"
selfPath :: Text
selfPath = "self"
reportsPath :: Text
reportsPath = "reports"
mkSimpleQueryItem :: Show a => Text -> a -> SimpleQueryItem
mkSimpleQueryItem s a = (encodeUtf8 s, encodeUtf8 $ pack (show a))
optionalQuery :: Show a => Text -> Maybe a -> [SimpleQueryItem]
optionalQuery t = maybe [] (return . mkSimpleQueryItem t)
mkProductQuery :: Maybe ProductId -> [SimpleQueryItem]
mkProductQuery = optionalQuery "product_id"
mkOrderIdQuery :: Maybe OrderId -> SimpleQuery
mkOrderIdQuery = optionalQuery "order_id"
encodeBody :: (ToJSON b) => b -> ByteString
encodeBody = LC8.toStrict . encode
-- | https://docs.pro.coinbase.com/#accounts
accounts :: CBAuthT ClientM [Account]
accounts = authRequest methodGet requestPath emptyBody API.accounts
where
requestPath = encodeRequestPath [accountsPath]
-- | https://docs.pro.coinbase.com/#get-an-account
account :: AccountId -> CBAuthT ClientM Account
account aid@(AccountId t) = authRequest methodGet requestPath emptyBody $ API.singleAccount aid
where
requestPath = encodeRequestPath [accountsPath, t]
-- | https://docs.pro.coinbase.com/#get-account-history
accountHistory :: AccountId -> CBAuthT ClientM [AccountHistory]
accountHistory aid@(AccountId t) = authRequest methodGet requestPath emptyBody $ API.accountHistory aid
where
ledgerPath = "ledger"
requestPath = encodeRequestPath [accountsPath, t, ledgerPath]
-- | https://docs.pro.coinbase.com/#get-holds
accountHolds :: AccountId -> CBAuthT ClientM [Hold]
accountHolds aid@(AccountId t) = authRequest methodGet requestPath emptyBody $ API.accountHolds aid
where
holdsPath = "holds"
requestPath = encodeRequestPath [accountsPath, t, holdsPath]
-- | https://docs.pro.coinbase.com/#list-orders
listOrders :: Maybe [Status] -> Maybe ProductId -> CBAuthT ClientM [Order]
listOrders st prid = authRequest methodGet requestPath emptyBody $ API.listOrders (defaultStatus st) prid
where
query = renderQuery True . simpleQueryToQuery $ orderQuery st prid
requestPath = encodeRequestPath [ordersPath] <> query
orderQuery :: Maybe [Status] -> Maybe ProductId -> SimpleQuery
orderQuery ss p = statusQuery ss <> mkProductQuery p
statusQuery :: Maybe [Status] -> [SimpleQueryItem]
statusQuery ss = mkSimpleQueryItem "status" <$> S.toList (unStatuses . statuses $ defaultStatus ss)
defaultStatus :: Maybe [Status] -> [Status]
defaultStatus = fromMaybe [All]
-- | https://docs.pro.coinbase.com/#get-an-order
getOrder :: OrderId -> CBAuthT ClientM Order
getOrder oid = authRequest methodGet requestPath emptyBody $ API.getOrder oid
where
requestPath = encodeRequestPath [ordersPath, unOrderId oid]
-- | https://docs.pro.coinbase.com/#get-an-order
getClientOrder :: ClientOrderId -> CBAuthT ClientM Order
getClientOrder cloid = authRequest methodGet requestPath emptyBody $ API.getClientOrder cloid
where
oid = toText $ unClientOrderId cloid
requestPath = encodeRequestPath [ordersPath, "client:" <> oid]
-- | https://docs.pro.coinbase.com/#place-a-new-order
placeOrder :: Maybe ClientOrderId
-> ProductId
-> Side
-> Maybe Size
-> Maybe Price
-> Maybe Bool
-> Maybe OrderType
-> Maybe STP
-> Maybe TimeInForce
-> CBAuthT ClientM Order
placeOrder clordid prid sd sz price po ot stp tif =
authRequest methodPost requestPath (encodeBody body) $ API.placeOrder body
where
requestPath = encodeRequestPath [ordersPath]
body = PlaceOrderBody clordid prid sd sz Nothing price po ot stp tif Nothing Nothing
-- | https://docs.pro.coinbase.com/#place-a-new-order
-- Use the underlying post body record
placeOrderBody :: PlaceOrderBody -> CBAuthT ClientM Order
placeOrderBody body =
authRequest methodPost requestPath (encodeBody body) $ API.placeOrder body
where
requestPath = encodeRequestPath [ordersPath]
-- | https://docs.pro.coinbase.com/#cancel-an-order
cancelOrder :: OrderId -> CBAuthT ClientM ()
cancelOrder oid = void . authRequest methodDelete requestPath emptyBody $ API.cancelOrder oid
where
requestPath = encodeRequestPath [ordersPath, unOrderId oid]
-- | https://docs.pro.coinbase.com/#cancel-all
cancelAll :: Maybe ProductId -> CBAuthT ClientM [OrderId]
cancelAll prid = authRequest methodDelete requestPath emptyBody (API.cancelAll prid)
where
query = renderQuery True . simpleQueryToQuery $ mkProductQuery prid
requestPath = encodeRequestPath [ordersPath] <> query
-- | https://docs.pro.coinbase.com/#fills
fills :: Maybe ProductId -> Maybe OrderId -> CBAuthT ClientM [Fill]
fills prid oid = authRequest methodGet requestPath emptyBody (API.fills prid oid)
where
fillsPath = "fills"
query = renderQuery True . simpleQueryToQuery $ mkSimpleQuery prid oid
requestPath = encodeRequestPath [fillsPath] <> query
mkSimpleQuery :: Maybe ProductId -> Maybe OrderId -> SimpleQuery
mkSimpleQuery p o = mkProductQuery p <> mkOrderIdQuery o
-- | https://docs.pro.coinbase.com/#get-current-fees
fees :: CBAuthT ClientM Fees
fees = authRequest methodGet feesRequestPath emptyBody API.fees
where
feesPath = "fees"
feesRequestPath = encodeRequestPath [feesPath]
-- | https://docs.pro.coinbase.com/#trailing-volume
trailingVolume :: CBAuthT ClientM [TrailingVolume]
trailingVolume = authRequest methodGet requestPath emptyBody API.trailingVolume
where
requestPath = encodeRequestPath [usersPath, selfPath, "trailing-volume"]
-- | https://docs.pro.coinbase.com/#get-current-exchange-limits
limits :: CBAuthT ClientM Limits
limits = authRequest methodGet requestPath emptyBody API.limits
where
requestPath = encodeRequestPath [usersPath, selfPath, "exchange-limits"]
-- | https://docs.pro.coinbase.com/#list-deposits
deposits :: Maybe ProfileId
-> Maybe UTCTime
-> Maybe UTCTime
-> Maybe Int
-> CBAuthT ClientM [Transfer]
deposits = transfers DepositTransferType
-- | https://docs.pro.coinbase.com/#list-withdrawals
withdrawals :: Maybe ProfileId
-> Maybe UTCTime
-> Maybe UTCTime
-> Maybe Int
-> CBAuthT ClientM [Transfer]
withdrawals = transfers WithdrawTransferType
transfers :: TransferType
-> Maybe ProfileId
-> Maybe UTCTime
-> Maybe UTCTime
-> Maybe Int
-> CBAuthT ClientM [Transfer]
transfers tt prof before after lm = authRequest methodGet requestPath emptyBody $
API.transfers tt prof before after lm
where
typeQ = return $ mkSimpleQueryItem "type" tt
profQ = optionalQuery "profile_id" prof
beforeQ = optionalQuery "before" before
afterQ = optionalQuery "after" after
lmQ = optionalQuery "limit" lm
query = renderQuery True . simpleQueryToQuery $ typeQ <> profQ <> beforeQ <> afterQ <> lmQ
requestPath = encodeRequestPath [transferPath <> "s"] <> query
-- | https://docs.pro.coinbase.com/#single-deposit
-- | https://docs.pro.coinbase.com/#single-withdrawal
transfer :: PaymentMethodId -> CBAuthT ClientM Transfer
transfer pmt@(PaymentMethodId p) = authRequest methodGet requestPath emptyBody $ API.transfer pmt
where
requestPath = encodeRequestPath [transferPath <> "s", p]
-- | https://docs.pro.coinbase.com/#payment-method
makeDeposit :: Double
-> Text
-> PaymentMethodId
-> CBAuthT ClientM DepositResponse
makeDeposit amt cur pmi =
authRequest methodPost requestPath (encodeBody body) $ API.makeDeposit body
where
requestPath = encodeRequestPath [depositsPath, "payment-method"]
body = DepositRequest amt cur pmi
-- | https://docs.pro.coinbase.com/#coinbase
makeCoinbaseDeposit :: Double
-> Text
-> AccountId
-> CBAuthT ClientM DepositResponse
makeCoinbaseDeposit amt cur act =
authRequest methodPost requestPath (encodeBody body) $ API.makeCoinbaseDeposit body
where
requestPath = encodeRequestPath [depositsPath, coinbaseAccountPath]
body = CoinbaseDepositRequest amt cur act
-- | https://docs.pro.coinbase.com/#generate-a-crypto-deposit-address
cryptoDepositAddress :: AccountId -> CBAuthT ClientM CryptoDepositAddress
cryptoDepositAddress act =
authRequest methodPost requestPath emptyBody $ API.cryptoDepositAddress act
where
requestPath = encodeRequestPath [coinbaseAccountPath <> "s", pack $ show act, "addresses"]
-- | https://docs.pro.coinbase.com/#payment-method55
makeWithdrawal :: Double
-> Text
-> PaymentMethodId
-> CBAuthT ClientM WithdrawalResponse
makeWithdrawal amt cur pmi =
authRequest methodPost requestPath (encodeBody body) $ API.makeWithdrawal body
where
requestPath = encodeRequestPath [withdrawalsPath, "payment-method"]
body = WithdrawalRequest amt cur pmi
-- | https://docs.pro.coinbase.com/#coinbase56
makeCoinbaseWithdrawal :: Double
-> Text
-> AccountId
-> CBAuthT ClientM WithdrawalResponse
makeCoinbaseWithdrawal amt cur act =
authRequest methodPost requestPath (encodeBody body) $ API.makeCoinbaseWithdrawal body
where
requestPath = encodeRequestPath [withdrawalsPath, coinbaseAccountPath]
body = CoinbaseWithdrawalRequest amt cur act
-- | https://docs.pro.coinbase.com/#crypto
makeCryptoWithdrawal :: Double
-> Text
-> Text
-> CBAuthT ClientM CryptoWithdrawalResponse
makeCryptoWithdrawal amt cur addr =
authRequest methodPost requestPath (encodeBody body) $ API.makeCryptoWithdrawal body
where
requestPath = encodeRequestPath [withdrawalsPath, "crypto"]
body = CryptoWithdrawalRequest amt cur addr
-- | https://docs.pro.coinbase.com/#fee-estimate
withdrawalFeeEstimate :: CurrencyType
-> CryptoAddress
-> CBAuthT ClientM WithdrawalFeeEstimateResponse
withdrawalFeeEstimate cur addr =
authRequest methodGet requestPath emptyBody $ API.withdrawalFeeEstimate cur addr
where
curQ = return $ mkSimpleQueryItem "currency" cur
addrQ = return $ mkSimpleQueryItem "crypto_address" addr
query = renderQuery True . simpleQueryToQuery $ curQ <> addrQ
requestPath = encodeRequestPath [withdrawalsPath, "fee-estimate"] <> query
-- | https://docs.pro.coinbase.com/#stablecoin-conversions
createStablecoinConversion :: CurrencyType -> CurrencyType -> Double -> CBAuthT ClientM StablecoinConversionResponse
createStablecoinConversion fromCur toCur amt =
authRequest methodPost requestPath (encodeBody body) $ API.createStablecoinConversion body
where
requestPath = encodeRequestPath ["conversions"]
body = StablecoinConversionRequest fromCur toCur amt
-- | https://docs.pro.coinbase.com/#list-payment-methods
paymentMethods :: CBAuthT ClientM [PaymentMethod]
paymentMethods = authRequest methodGet requestPath emptyBody API.paymentMethods
where
requestPath = encodeRequestPath ["payment-methods"]
-- | https://docs.pro.coinbase.com/#list-accounts64
coinbaseAccounts :: CBAuthT ClientM [CoinbaseAccount]
coinbaseAccounts = authRequest methodGet requestPath emptyBody API.coinbaseAccounts
where
requestPath = encodeRequestPath [coinbaseAccountPath <> "s"]
-- | https://docs.pro.coinbase.com/#list-profiles
profiles :: Maybe Bool -> CBAuthT ClientM [Profile]
profiles active = authRequest methodGet requestPath emptyBody $ API.profiles active
where
activeQ = optionalQuery "active" active
query = renderQuery True . simpleQueryToQuery $ activeQ
requestPath = encodeRequestPath [profilesPath] <> query
-- | https://docs.pro.coinbase.com/#get-a-profile
profile :: ProfileId -> CBAuthT ClientM Profile
profile profId = authRequest methodGet requestPath emptyBody $ API.profile profId
where
requestPath = encodeRequestPath [profilesPath, profId]
-- | https://docs.pro.coinbase.com/#create-profile-transfer
profileTransfer :: ProfileId
-> ProfileId
-> CurrencyType
-> String
-> CBAuthT ClientM ()
profileTransfer fromProf toProf cur amt = void . authRequest methodPost requestPath (encodeBody body) $ API.profileTransfer body
where
requestPath = encodeRequestPath [profilesPath, transferPath]
body = ProfileTransfer fromProf toProf cur amt
-- | https://docs.pro.coinbase.com/#create-a-new-report
createReport :: ReportRequest -> CBAuthT ClientM ReportResponse
createReport req = authRequest methodPost requestPath (encodeBody req) $ API.createReport req
where
requestPath = encodeRequestPath [reportsPath]
-- | https://docs.pro.coinbase.com/#get-report-status
getReport :: ReportId -> CBAuthT ClientM ReportResponse
getReport rid = authRequest methodGet requestPath emptyBody $ API.getReport rid
where
requestPath = encodeRequestPath [reportsPath, toText $ unReportId rid]
-- | https://docs.pro.coinbase.com/#oracle
getOracle :: CBAuthT ClientM OracleResponse
getOracle = authRequest methodGet requestPath emptyBody API.getOracle
where
requestPath = encodeRequestPath ["oracle"]