You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
179 lines
5.7 KiB
179 lines
5.7 KiB
|
9 years ago
|
|
||
|
|
module QuoteSource.TableParsers.AllParamsTableParser (
|
||
|
|
AllParamsTableParser,
|
||
|
|
mkAllParamsTableParser
|
||
|
|
) where
|
||
|
|
|
||
|
|
import qualified Data.Map.Lazy as M
|
||
|
|
import QuoteSource.TableParser
|
||
|
|
import Data.ATrade
|
||
|
|
import QuoteSource.XlParser
|
||
|
|
import Data.Tuple
|
||
|
|
import Data.Decimal
|
||
|
|
import Control.Monad.State.Strict
|
||
|
|
import Data.Time.Clock
|
||
|
|
import Data.Maybe
|
||
|
|
import Data.DateTime
|
||
|
|
|
||
|
|
data TableColumn = CUnknown
|
||
|
|
| CTicker
|
||
|
|
| CClassCode
|
||
|
|
| CPrice
|
||
|
|
| CBestBid
|
||
|
|
| CBestAsk
|
||
|
|
| CTotalSupply
|
||
|
|
| CTotalDemand
|
||
|
|
| COpenInterest
|
||
|
|
| CVolume
|
||
|
|
deriving (Eq, Show, Ord)
|
||
|
|
|
||
|
|
columnCodes = M.fromList [
|
||
|
|
("CLASS_CODE", CClassCode),
|
||
|
|
("CODE", CTicker),
|
||
|
|
("bid", CBestBid),
|
||
|
|
("offer", CBestAsk),
|
||
|
|
("last", CPrice),
|
||
|
|
("numcontracts", COpenInterest),
|
||
|
|
("biddepth", CTotalDemand),
|
||
|
|
("offerdepth", CTotalSupply),
|
||
|
|
("voltoday", CVolume)]
|
||
|
|
|
||
|
|
columnToDataType :: TableColumn -> DataType
|
||
|
|
columnToDataType x
|
||
|
|
| x == CPrice = Price
|
||
|
|
| x == CBestBid = BestBid
|
||
|
|
| x == CBestAsk = BestOffer
|
||
|
|
| x == CTotalSupply = TotalSupply
|
||
|
|
| x == CTotalDemand = TotalDemand
|
||
|
|
| x == COpenInterest = OpenInterest
|
||
|
|
| otherwise = Unknown
|
||
|
|
|
||
|
|
|
||
|
|
type TableSchema = M.Map TableColumn Int
|
||
|
|
|
||
|
|
data AllParamsTableParser = AllParamsTableParser {
|
||
|
|
schema :: Maybe TableSchema,
|
||
|
|
tableId :: String,
|
||
|
|
volumes :: M.Map String Integer,
|
||
|
|
timestampHint :: UTCTime
|
||
|
|
}
|
||
|
|
|
||
|
|
mkAllParamsTableParser id = AllParamsTableParser {
|
||
|
|
schema = Nothing,
|
||
|
|
tableId = id,
|
||
|
|
volumes = M.empty,
|
||
|
|
timestampHint = startOfTime }
|
||
|
|
|
||
|
|
securityName :: String -> String -> String
|
||
|
|
securityName classCode ticker = classCode ++ ('#' : ticker)
|
||
|
|
|
||
|
|
parseSchema (width, height, cells) = M.fromList . zipWith (curry swap) [0..] $ map parseSchemaItem . take width $ cells
|
||
|
|
where
|
||
|
|
parseSchemaItem cell = case cell of
|
||
|
|
XlString s -> M.findWithDefault CUnknown s columnCodes
|
||
|
|
_ -> CUnknown
|
||
|
|
|
||
|
|
safeAt :: [a] -> Int -> Maybe a
|
||
|
|
safeAt list index = if index < 0 || index >= length list
|
||
|
|
then Nothing
|
||
|
|
else Just $ list !! index
|
||
|
|
|
||
|
|
parseWithSchema :: TableSchema -> (Int, Int, [XlData]) -> State AllParamsTableParser [Tick]
|
||
|
|
parseWithSchema sch (width, height, cells) = do
|
||
|
|
ticks <- mapM parseRow $ groupByN width $ cells
|
||
|
|
return $ concat ticks
|
||
|
|
where
|
||
|
|
parseRow :: [XlData] -> State AllParamsTableParser [Tick]
|
||
|
|
parseRow row = case (getClassCode row, getTicker row) of
|
||
|
|
(Just classCode, Just ticker) -> do
|
||
|
|
maybeticks <- mapM (\f -> f row classCode ticker) parsers
|
||
|
|
return $ catMaybes maybeticks
|
||
|
|
_ -> return []
|
||
|
|
|
||
|
|
parsers :: [[XlData] -> String -> String -> State AllParamsTableParser (Maybe Tick)]
|
||
|
|
parsers = parsePrice : map parseValue [CBestBid, CBestAsk, COpenInterest, CTotalDemand, CTotalSupply]
|
||
|
|
|
||
|
|
parseValue :: TableColumn -> [XlData] -> String -> String -> State AllParamsTableParser (Maybe Tick)
|
||
|
|
parseValue columnType row classCode ticker = case M.lookup columnType sch of
|
||
|
|
Nothing -> return Nothing
|
||
|
|
Just index -> case row `safeAt` index of
|
||
|
|
Just (XlDouble value) -> do
|
||
|
|
ts <- gets timestampHint
|
||
|
|
return $ Just Tick {
|
||
|
|
security = securityName classCode ticker,
|
||
|
|
datatype = columnToDataType columnType,
|
||
|
|
timestamp = ts,
|
||
|
|
value = realFracToDecimal 10 value,
|
||
|
|
volume = 0 }
|
||
|
|
_ -> return Nothing
|
||
|
|
|
||
|
|
parsePrice :: [XlData] -> String -> String -> State AllParamsTableParser (Maybe Tick)
|
||
|
|
parsePrice row classCode ticker = case M.lookup CPrice sch of
|
||
|
|
Nothing -> return Nothing
|
||
|
|
Just index -> case row `safeAt` index of
|
||
|
|
Just (XlDouble value) -> do
|
||
|
|
tickVolume <- calculateTickVolume row $ securityName classCode ticker
|
||
|
|
if tickVolume > 0
|
||
|
|
then do
|
||
|
|
ts <- gets timestampHint
|
||
|
|
return $ Just Tick {
|
||
|
|
security = securityName classCode ticker,
|
||
|
|
datatype = Price,
|
||
|
|
timestamp = ts,
|
||
|
|
value = realFracToDecimal 10 value,
|
||
|
|
volume = tickVolume}
|
||
|
|
else
|
||
|
|
return Nothing
|
||
|
|
_ -> return Nothing
|
||
|
|
|
||
|
|
calculateTickVolume :: [XlData] -> String -> State AllParamsTableParser Integer
|
||
|
|
calculateTickVolume row secname = case M.lookup CVolume sch of
|
||
|
|
Nothing -> return 1
|
||
|
|
Just index -> case row `safeAt` index of
|
||
|
|
Just (XlDouble volume) -> do
|
||
|
|
oldVolumes <- gets volumes
|
||
|
|
let intVolume = round volume
|
||
|
|
case M.lookup secname oldVolumes of
|
||
|
|
Nothing -> do
|
||
|
|
modify (\s -> s { volumes = M.insert secname intVolume oldVolumes } )
|
||
|
|
return 1
|
||
|
|
Just oldVolume -> do
|
||
|
|
modify (\s -> s { volumes = M.insert secname intVolume oldVolumes } )
|
||
|
|
return $ if intVolume > oldVolume
|
||
|
|
then intVolume - oldVolume
|
||
|
|
else if intVolume < oldVolume
|
||
|
|
then 1
|
||
|
|
else 0
|
||
|
|
_ -> return 0
|
||
|
|
|
||
|
|
groupByN :: Int -> [a] -> [[a]]
|
||
|
|
groupByN n l = case l of
|
||
|
|
[] -> []
|
||
|
|
_ -> take n l : groupByN n (drop n l)
|
||
|
|
|
||
|
|
getStringField :: TableColumn -> [XlData] -> Maybe String
|
||
|
|
getStringField columnType row = case M.lookup columnType sch of
|
||
|
|
Nothing -> Nothing
|
||
|
|
Just index -> case row `safeAt` index of
|
||
|
|
Just (XlString s) -> Just s
|
||
|
|
_ -> Nothing
|
||
|
|
|
||
|
|
getClassCode :: [XlData] -> Maybe String
|
||
|
|
getClassCode = getStringField CClassCode
|
||
|
|
|
||
|
|
getTicker :: [XlData] -> Maybe String
|
||
|
|
getTicker = getStringField CTicker
|
||
|
|
|
||
|
|
instance TableParser AllParamsTableParser where
|
||
|
|
parseXlTable table = do
|
||
|
|
mySchema <- gets schema
|
||
|
|
case mySchema of
|
||
|
|
Just sch -> parseWithSchema sch table
|
||
|
|
Nothing -> do
|
||
|
|
modify (\s -> s { schema = Just $ parseSchema table })
|
||
|
|
parseWithSchema (parseSchema table) table
|
||
|
|
|
||
|
|
getTableId = tableId
|
||
|
|
giveTimestampHint tp hint = tp { timestampHint = hint }
|
||
|
|
|