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 })