8 changed files with 376 additions and 32 deletions
@ -0,0 +1,55 @@
@@ -0,0 +1,55 @@
|
||||
|
||||
module Data.ATrade ( |
||||
Tick(..), |
||||
DataType(..) |
||||
) where |
||||
|
||||
import Data.Decimal |
||||
import Data.Time.Clock |
||||
|
||||
data DataType = Unknown |
||||
| Price |
||||
| OpenInterest |
||||
| BestBid |
||||
| BestOffer |
||||
| Depth |
||||
| TheoryPrice |
||||
| Volatility |
||||
| TotalSupply |
||||
| TotalDemand |
||||
deriving (Show, Eq) |
||||
|
||||
instance Enum DataType where |
||||
fromEnum x |
||||
| x == Price = 1 |
||||
| x == OpenInterest = 3 |
||||
| x == BestBid = 4 |
||||
| x == BestOffer = 5 |
||||
| x == Depth = 6 |
||||
| x == TheoryPrice = 7 |
||||
| x == Volatility = 8 |
||||
| x == TotalSupply = 9 |
||||
| x == TotalDemand = 10 |
||||
| x == Unknown = -1 |
||||
| otherwise = -1 |
||||
|
||||
toEnum x |
||||
| x == 1 = Price |
||||
| x == 3 = OpenInterest |
||||
| x == 4 = BestBid |
||||
| x == 5 = BestOffer |
||||
| x == 6 = Depth |
||||
| x == 7 = TheoryPrice |
||||
| x == 8 = Volatility |
||||
| x == 9 = TotalSupply |
||||
| x == 10 = TotalDemand |
||||
| otherwise = Unknown |
||||
|
||||
data Tick = Tick { |
||||
security :: String, |
||||
datatype :: DataType, |
||||
timestamp :: UTCTime, |
||||
value :: Decimal, |
||||
volume :: Integer |
||||
} deriving (Show, Eq) |
||||
|
||||
@ -1,4 +1,15 @@
@@ -1,4 +1,15 @@
|
||||
|
||||
module QuoteSource.TableParser ( |
||||
TableParser(..) |
||||
) where |
||||
|
||||
import QuoteSource.XlParser |
||||
import Data.ATrade |
||||
import Control.Monad.State.Strict |
||||
import Data.Time.Clock |
||||
|
||||
class TableParser a where |
||||
parseXlTable :: (Int, Int, [XlData]) -> State a [Tick] |
||||
giveTimestampHint :: a -> UTCTime -> a |
||||
getTableId :: a -> String |
||||
|
||||
|
||||
@ -0,0 +1,178 @@
@@ -0,0 +1,178 @@
|
||||
|
||||
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 } |
||||
|
||||
Loading…
Reference in new issue