{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} module Coinbase.Exchange.Types.Private where import Control.Applicative import Control.DeepSeq import Control.Monad import Data.Aeson.Casing import Data.Aeson.Types import Data.Char import Data.Data import Data.Hashable import Data.Text (Text) import qualified Data.Text as T import Data.Time import Data.UUID import Data.Word import GHC.Generics import Coinbase.Exchange.Types import Coinbase.Exchange.Types.Core -- Accounts newtype AccountId = AccountId { unAccountId :: UUID } deriving (Eq, Ord, Show, Read, Data, Typeable, Generic, NFData, Hashable, FromJSON, ToJSON) data Account = Account { accId :: AccountId , accBalance :: CoinScientific , accHold :: CoinScientific , accAvailable :: CoinScientific , accCurrency :: CurrencyId } deriving (Show, Eq, Data, Typeable, Generic) instance NFData Account instance ToJSON Account where toJSON = genericToJSON coinbaseAesonOptions instance FromJSON Account where parseJSON = genericParseJSON coinbaseAesonOptions -- newtype EntryId = EntryId { unEntryId :: Word64 } deriving (Eq, Ord, Num, Show, Read, Data, Typeable, Generic, NFData, Hashable, FromJSON, ToJSON) data Entry = Entry { entryId :: EntryId , entryCreatedAt :: UTCTime , entryAmount :: CoinScientific , entryBalance :: CoinScientific , entryType :: EntryType , entryDetails :: EntryDetails } deriving (Show, Data, Typeable, Generic) instance NFData Entry instance ToJSON Entry where toJSON Entry{..} = object [ "id" .= entryId , "created_at" .= entryCreatedAt , "amount" .= entryAmount , "balance" .= entryBalance , "type" .= entryType , "details" .= entryDetails ] instance FromJSON Entry where parseJSON (Object m) = Entry <$> m .: "id" <*> m .: "created_at" <*> m .: "amount" <*> m .: "balance" <*> m .: "type" <*> m .: "details" parseJSON _ = mzero data EntryType = Match | Fee | Transfer deriving (Eq, Ord, Show, Read, Data, Typeable, Generic) instance NFData EntryType instance Hashable EntryType instance ToJSON EntryType where toJSON = genericToJSON defaultOptions { constructorTagModifier = map toLower } instance FromJSON EntryType where parseJSON = genericParseJSON defaultOptions { constructorTagModifier = map toLower } data EntryDetails = EntryDetails { detailOrderId :: Maybe OrderId , detailTradeId :: Maybe TradeId , detailProductId :: Maybe ProductId } deriving (Show, Data, Typeable, Generic) instance NFData EntryDetails instance ToJSON EntryDetails where toJSON = genericToJSON coinbaseAesonOptions instance FromJSON EntryDetails where parseJSON = genericParseJSON coinbaseAesonOptions -- newtype HoldId = HoldId { unHoldId :: UUID } deriving (Eq, Ord, Show, Read, Data, Typeable, Generic, NFData, Hashable, FromJSON, ToJSON) data Hold = OrderHold { holdId :: HoldId , holdAccountId :: AccountId , holdCreatedAt :: UTCTime , holdUpdatedAt :: UTCTime , holdAmount :: CoinScientific , holdOrderRef :: OrderId } | TransferHold { holdId :: HoldId , holdAccountId :: AccountId , holdCreatedAt :: UTCTime , holdUpdatedAt :: UTCTime , holdAmount :: CoinScientific , holdTransferRef :: TransferId } deriving (Show, Data, Typeable, Generic) instance NFData Hold instance ToJSON Hold where toJSON = genericToJSON coinbaseAesonOptions instance FromJSON Hold where parseJSON = genericParseJSON coinbaseAesonOptions -- Orders data OrderContigency = GoodTillCanceled | ImmediateOrCancel | FillOrKill deriving (Eq, Ord, Show, Read, Data, Typeable, Generic) instance NFData OrderContigency instance Hashable OrderContigency instance ToJSON OrderContigency where toJSON GoodTillCanceled = String "GTC" toJSON ImmediateOrCancel = String "IOC" toJSON FillOrKill = String "FOK" instance FromJSON OrderContigency where parseJSON (String "GTC") = return GoodTillCanceled parseJSON (String "IOC") = return ImmediateOrCancel parseJSON (String "FOK") = return FillOrKill parseJSON _ = mzero data SelfTrade = DecrementAndCancel | CancelOldest | CancelNewest | CancelBoth deriving (Eq, Ord, Show, Read, Data, Typeable, Generic) instance NFData SelfTrade instance Hashable SelfTrade instance ToJSON SelfTrade where toJSON DecrementAndCancel = String "dc" toJSON CancelOldest = String "co" toJSON CancelNewest = String "cn" toJSON CancelBoth = String "cb" instance FromJSON SelfTrade where parseJSON (String "dc") = return DecrementAndCancel parseJSON (String "co") = return CancelOldest parseJSON (String "cn") = return CancelNewest parseJSON (String "cb") = return CancelBoth parseJSON _ = mzero data NewOrder = NewLimitOrder { noProductId :: ProductId , noSide :: Side , noSelfTrade :: SelfTrade , noClientOid :: Maybe ClientOrderId --- , noPrice :: Price , noSize :: Size ,noTimeInForce:: OrderContigency , noPostOnly :: Bool } | NewMarketOrder { noProductId :: ProductId , noSide :: Side , noSelfTrade :: SelfTrade , noClientOid :: Maybe ClientOrderId --- , noSizeAndOrFunds :: Either Size (Maybe Size, Cost) } deriving (Show, Data, Typeable, Generic) instance NFData NewOrder instance ToJSON NewOrder where toJSON NewLimitOrder{..} = object ([ "type" .= ("limit" :: Text) , "product_id" .= noProductId , "side" .= noSide , "stp" .= noSelfTrade , "price" .= noPrice , "size" .= noSize , "time_in_force" .= noTimeInForce , "post_only" .= noPostOnly ] ++ clientID ) where clientID = case noClientOid of Just cid -> [ "client_oid" .= cid ] Nothing -> [] toJSON NewMarketOrder{..} = object ([ "type" .= ("market" :: Text) , "product_id" .= noProductId , "side" .= noSide , "stp" .= noSelfTrade ] ++ clientID ++ size ++ funds ) where clientID = case noClientOid of Just cid -> [ "client_oid" .= cid ] Nothing -> [] (size,funds) = case noSizeAndOrFunds of Left s -> (["size" .= s],[]) Right (ms,f) -> case ms of Nothing -> ( [] , ["funds" .= f] ) Just s' -> ( ["size" .= s'], ["funds" .= f] ) data OrderConfirmation = OrderConfirmation { ocId :: OrderId } deriving (Show, Data, Typeable, Generic) instance NFData OrderConfirmation instance ToJSON OrderConfirmation where toJSON = genericToJSON coinbaseAesonOptions instance FromJSON OrderConfirmation where parseJSON = genericParseJSON coinbaseAesonOptions data Order = LimitOrder { orderId :: OrderId , orderProductId :: ProductId , orderStatus :: OrderStatus , orderSelfTrade :: SelfTrade , orderSettled :: Bool , orderSide :: Side , orderCreatedAt :: UTCTime , orderFilledSize :: Maybe Size , orderFilledFees :: Maybe Price , orderDoneAt :: Maybe UTCTime , orderDoneReason :: Maybe Reason , orderPrice :: Price , orderSize :: Size , orderTimeInForce:: OrderContigency , orderPostOnly :: Bool } | MarketOrder { orderId :: OrderId , orderProductId :: ProductId , orderStatus :: OrderStatus , orderSelfTrade :: SelfTrade , orderSettled :: Bool , orderSide :: Side , orderCreatedAt :: UTCTime , orderFilledSize :: Maybe Size , orderFilledFees :: Maybe Price , orderDoneAt :: Maybe UTCTime , orderDoneReason :: Maybe Reason , orderSizeAndOrFunds :: Either Size (Maybe Size, Cost) } deriving (Show, Data, Typeable, Generic) instance NFData Order instance ToJSON Order where toJSON LimitOrder{..} = object [ "type" .= ("limit" :: Text) , "id" .= orderId , "product_id" .= orderProductId , "status" .= orderStatus , "stp" .= orderSelfTrade , "settled" .= orderSettled , "side" .= orderSide , "created_at" .= orderCreatedAt , "filled_size" .= orderFilledSize , "filled_fees" .= orderFilledFees , "done_at" .= orderDoneAt , "done_reason" .= orderDoneReason , "price" .= orderPrice , "size" .= orderSize , "time_in_force" .= orderTimeInForce , "post_only" .= orderPostOnly ] toJSON MarketOrder{..} = object ([ "type" .= ("market" :: Text) , "id" .= orderId , "product_id" .= orderProductId , "status" .= orderStatus , "stp" .= orderSelfTrade , "settled" .= orderSettled , "side" .= orderSide , "created_at" .= orderCreatedAt , "filled_size" .= orderFilledSize , "filled_fees" .= orderFilledFees , "done_at" .= orderDoneAt , "done_reason" .= orderDoneReason ] ++ size ++ funds ) where (size,funds) = case orderSizeAndOrFunds of Left s -> (["size" .= s],[]) Right (ms,f) -> case ms of Nothing -> ( [] , ["funds" .= f] ) Just s' -> ( ["size" .= s'], ["funds" .= f] ) instance FromJSON Order where parseJSON (Object m) = do ordertype <- m .: "type" case (ordertype :: String) of "limit" -> LimitOrder <$> m .: "id" <*> m .: "product_id" <*> m .: "status" <*> m .: "stp" <*> m .: "settled" <*> m .: "side" <*> m .: "created_at" <*> m .:? "filled_size" <*> m .:? "filled_fees" <*> m .:? "done_at" <*> m .:? "done_reason" <*> m .: "price" <*> m .: "size" <*> m .:? "time_in_force" .!= GoodTillCanceled -- older orders don't seem to have this field <*> m .: "post_only" "market" -> MarketOrder <$> m .: "id" <*> m .: "product_id" <*> m .: "status" <*> m .: "stp" <*> m .: "settled" <*> m .: "side" <*> m .: "created_at" <*> m .:? "filled_size" <*> m .:? "filled_fees" <*> m .:? "done_at" <*> m .:? "done_reason" <*> (do ms <- m .:? "size" mf <- m .:? "funds" case (ms,mf) of (Nothing, Nothing) -> mzero (Just s , Nothing) -> return $ Left s (Nothing, Just f ) -> return $ Right (Nothing, f) (Just s , Just f ) -> return $ Right (Just s , f) ) _ -> mzero parseJSON _ = mzero -- Fills data Liquidity = Maker | Taker deriving (Eq, Ord, Show, Read, Data, Typeable, Generic) instance NFData Liquidity instance Hashable Liquidity instance ToJSON Liquidity where toJSON Maker = String "M" toJSON Taker = String "T" instance FromJSON Liquidity where parseJSON (String "M") = return Maker parseJSON (String "T") = return Taker parseJSON _ = mzero data Fill = Fill { fillTradeId :: TradeId , fillProductId :: ProductId , fillPrice :: Price , fillSize :: Size , fillOrderId :: OrderId , fillCreatedAt :: UTCTime , fillLiquidity :: Liquidity , fillFee :: Price , fillSettled :: Bool , fillSide :: Side } deriving (Show, Data, Typeable, Generic) instance NFData Fill instance ToJSON Fill where toJSON Fill{..} = object [ "trade_id" .= fillTradeId , "product_id" .= fillProductId , "price" .= fillPrice , "size" .= fillSize , "order_id" .= fillOrderId , "created_at" .= fillCreatedAt , "liquidity" .= fillLiquidity , "fee" .= fillFee , "settled" .= fillSettled , "side" .= fillSide ] instance FromJSON Fill where parseJSON (Object m) = Fill <$> m .: "trade_id" <*> m .: "product_id" <*> m .: "price" <*> m .: "size" <*> m .: "order_id" <*> m .: "created_at" <*> m .: "liquidity" <*> m .: "fee" <*> m .: "settled" <*> m .: "side" parseJSON _ = mzero -- Transfers newtype TransferId = TransferId { unTransferId :: UUID } deriving (Eq, Ord, Show, Read, Data, Typeable, Generic, NFData, FromJSON, ToJSON) newtype CoinbaseAccountId = CoinbaseAccountId { unCoinbaseAccountId :: UUID } deriving (Eq, Ord, Show, Read, Data, Typeable, Generic, NFData, FromJSON, ToJSON) data Transfer = Deposit { transAmount :: Size , transCoinbaseAccount :: CoinbaseAccountId } | Withdraw { transAmount :: Size , transCoinbaseAccount :: CoinbaseAccountId } deriving (Show, Data, Typeable, Generic) instance NFData Transfer instance ToJSON Transfer where toJSON = genericToJSON coinbaseAesonOptions instance FromJSON Transfer where parseJSON = genericParseJSON coinbaseAesonOptions