gemini-exports-0.1.0.0: src/Console/Gemini/Exports/Csv.hs
{-# LANGUAGE RecordWildCards #-}
{-| Types & functions for converting Gemini API responses into CSV exports.
-}
module Console.Gemini.Exports.Csv
( ExportData(..)
, makeExportData
, makeExportCsv
, ExportLine(..)
, getExportLineTimestamp
) where
import Control.Applicative ( (<|>) )
import Control.Monad.IO.Class ( MonadIO(..) )
import Data.Csv ( (.=)
, DefaultOrdered(..)
, ToNamedRecord(..)
, defaultEncodeOptions
, encUseCrLf
, encodeDefaultOrderedByNameWith
, header
, namedRecord
)
import Data.Maybe ( fromMaybe )
import Data.Scientific ( FPFormat(Fixed)
, Scientific
, formatScientific
)
import Data.Text ( Text
, empty
, pack
)
import Data.Time ( TimeZone
, defaultTimeLocale
, formatTime
, getTimeZone
, utcToZonedTime
)
import Data.Time.Clock.POSIX ( POSIXTime
, posixSecondsToUTCTime
)
import Web.Gemini
import qualified Data.ByteString.Lazy.Char8 as LBS
-- | The data required for rendering a single CSV row.
data ExportData = ExportData
{ edTZ :: TimeZone
, edLine :: ExportLine
}
deriving (Show, Read, Eq, Ord)
instance DefaultOrdered ExportData where
headerOrder _ = header
[ "time"
, "base-asset"
, "quote-asset"
, "type"
, "description"
, "price"
, "quantity"
, "total"
, "fee"
, "fee-currency"
, "trade-id"
]
instance ToNamedRecord ExportData where
toNamedRecord (ExportData tz lineData) = namedRecord $ case lineData of
TradeExport Trade {..} SymbolDetails {..} ->
[ "time" .= formatTimestamp tTimestamp
, "base-asset" .= sdBaseCurrency
, "quote-asset" .= sdQuoteCurrency
, "type" .= if tIsBuy then "Buy" else ("Sell" :: Text)
, "description" .= empty
, "price" .= formatDecimal tPrice
, "quantity" .= formatDecimal tAmount
, "total" .= formatDecimal (tPrice * tAmount)
, "fee" .= formatDecimal tFeeAmount
, "fee-currency" .= tFeeCurrency
, "trade-id" .= tId
]
TransferExport Transfer {..} ->
[ "time" .= formatTimestamp trTimestamp
, "base-asset" .= trCurrency
, "quote-asset" .= empty
, "type" .= trType
, "description" .= toDescr (trMethod, trPurpose)
, "price" .= empty
, "quantity" .= formatDecimal trAmount
, "total" .= formatDecimal trAmount
, "fee" .= empty
, "fee-currency" .= empty
, "trade-id" .= trId
]
EarnExport EarnTransaction {..} ->
[ "time" .= formatTimestamp etTimestamp
, "base-asset" .= etAmountCurrency
, "quote-asset" .= fromMaybe empty etPriceCurrency
, "type" .= ("Earn " <> etType)
, "description" .= empty
, "price" .= maybe empty formatDecimal etPrice
, "quantity" .= formatDecimal etAmount
, "total" .= maybe empty (formatDecimal . (* etAmount)) etPrice
, "fee" .= empty
, "fee-currency" .= empty
, "trade-id" .= etId
]
where
-- Render a transfer description by combining the optional method
-- & purpose fields.
toDescr :: (Maybe Text, Maybe Text) -> Text
toDescr = \case
(Just m, Just p) -> m <> " " <> p
(m , p ) -> fromMaybe "" $ m <|> p
-- Convert a timestamp into a localtime with the line's timezone
-- & render it in `YYYY-MM-DD HH:MM:SS.nnnnnnnnn` format.`
formatTimestamp :: POSIXTime -> String
formatTimestamp =
formatTime defaultTimeLocale "%F %T%Q"
. utcToZonedTime tz
. posixSecondsToUTCTime
-- Render a decimal number with the minimum precision neccesary..
formatDecimal :: Scientific -> Text
formatDecimal = pack . formatScientific Fixed Nothing
-- | Determine the 'TimeZone' for the 'ExportLine' & return both as an
-- 'ExportData'.
makeExportData :: MonadIO m => ExportLine -> m ExportData
makeExportData lineData = do
tz <- liftIO . getTimeZone . posixSecondsToUTCTime $ getExportLineTimestamp
lineData
return $ ExportData tz lineData
-- | Render the export data as a CSV with a header row.
makeExportCsv :: [ExportData] -> LBS.ByteString
makeExportCsv =
encodeDefaultOrderedByNameWith (defaultEncodeOptions { encUseCrLf = False })
-- | Split out the data required for different export line types.
data ExportLine
= TradeExport Trade SymbolDetails
| TransferExport Transfer
| EarnExport EarnTransaction
deriving (Show, Read, Eq, Ord)
-- | Get the timestamp field of an 'ExportLine'.
getExportLineTimestamp :: ExportLine -> POSIXTime
getExportLineTimestamp = \case
TradeExport t _ -> tTimestamp t
TransferExport t -> trTimestamp t
EarnExport t -> etTimestamp t