packages feed

binance-exports-0.1.0.0: src/Console/Binance/Exports/Csv.hs

{-# LANGUAGE RecordWildCards #-}
{- | Types & CSV serialization for the exports.
-}
module Console.Binance.Exports.Csv
    ( TradeExportData(..)
    , buildTradeExport
    ) where
import           Data.Csv                       ( (.=)
                                                , DefaultOrdered(..)
                                                , ToNamedRecord(..)
                                                , defaultEncodeOptions
                                                , encUseCrLf
                                                , encodeDefaultOrderedByNameWith
                                                , header
                                                , namedRecord
                                                )
import           Data.Scientific                ( FPFormat(..)
                                                , Scientific
                                                , formatScientific
                                                , isInteger
                                                )
import           Data.Time                      ( defaultTimeLocale
                                                , formatTime
                                                )
import           Data.Time.Clock.POSIX          ( posixSecondsToUTCTime )

import qualified Data.ByteString.Lazy          as LBS
import           Web.Binance                    ( SymbolDetails(..)
                                                , Trade(..)
                                                )


-- | We need both the 'SymbolDetails' & the 'Trade' to generate an export
-- line.
data TradeExportData = TradeExportData
    { tedSymbol :: SymbolDetails
    , tedTrade  :: Trade
    }
    deriving (Show, Read, Eq, Ord)

-- | We match the format of the old @Trade History@ export as much as
-- possible, but use the asset precisions for the @price@ & @quantity@
-- fields & output the @trade-id@ as well.
instance ToNamedRecord TradeExportData where
    toNamedRecord (TradeExportData SymbolDetails {..} Trade {..}) = namedRecord
        [ "time" .= formatTime defaultTimeLocale
                               "%F %T"
                               (posixSecondsToUTCTime tTime)
        , "base-asset" .= sdBaseAsset
        , "quote-asset" .= sdQuoteAsset
        , "type" .= if tIsBuyer then "BUY" else ("SELL" :: String)
        , "price" .= formatScientific Fixed (Just sdQuoteAssetPrecision) tPrice
        , "quantity"
            .= formatScientific Fixed (Just sdBaseAssetPrecision) tQuantity
        , "total" .= renderScientific (tPrice * tQuantity)
        , "fee" .= renderScientific tCommission
        , "fee-currency" .= tCommissionAsset
        , "trade-id" .= tId
        ]
      where
        -- | Render as an integer if possible, otherwise render to the
        -- minimum decimal precision needed.
        renderScientific :: Scientific -> String
        renderScientific p = if isInteger p
            then formatScientific Fixed (Just 0) p
            else formatScientific Fixed Nothing p

instance DefaultOrdered TradeExportData where
    headerOrder _ = header
        [ "time"
        , "base-asset"
        , "quote-asset"
        , "type"
        , "price"
        , "quantity"
        , "total"
        , "fee"
        , "fee-currency"
        , "trade-id"
        ]

-- | Generate a CSV from the trade data.
buildTradeExport :: [TradeExportData] -> LBS.ByteString
buildTradeExport =
    encodeDefaultOrderedByNameWith (defaultEncodeOptions { encUseCrLf = False })