stooq-api 0.3.1.0 → 0.4.1.0
raw patch · 4 files changed
+50/−22 lines, 4 filesdep +cassavadep −aesondep −stringsearchdep ~vector
Dependencies added: cassava
Dependencies removed: aeson, stringsearch
Dependency ranges changed: vector
Files
- CHANGELOG.md +4/−0
- src/lib/Web/Data/Stooq/API.hs +6/−5
- src/utils/Web/Data/Stooq/Internals.hs +35/−11
- stooq-api.cabal +5/−6
CHANGELOG.md view
@@ -1,5 +1,9 @@ # Revision history for stooq +## 0.4.0.0 -- 2022-11-27++* Switching to XML format, which seems more reliable than JSON.+ ## 0.3.1.0 -- 2022-07-07 * Migrating to nixpkgs 22.05.
src/lib/Web/Data/Stooq/API.hs view
@@ -49,9 +49,10 @@ -- | Sends a request for the specified ticker and returns its latest price. -- Returns "Nothing" if the response is invalid (this is most likely due to using a non-existent ticker).-fetchPrice :: StooqSymbol -> IO (Maybe [StooqPrice])+fetchPrice :: StooqSymbol -> IO (Either String [StooqPrice]) fetchPrice ticker = do- r <- get (queryUrl ticker)+ let url = queryUrl ticker+ r <- get url return $ fmap (map toApiType . Impl.symbols) (Impl.parseResponse (r ^. responseBody)) where@@ -59,7 +60,7 @@ baseUrl = "https://stooq.pl/q/l/?s=" queryUrl :: StooqSymbol -> String- queryUrl (StooqSymbol ticker) = baseUrl ++ ticker ++ "&e=json"+ queryUrl (StooqSymbol ticker) = baseUrl ++ ticker ++ "&e=xml" toApiType :: Impl.StooqRow -> StooqPrice toApiType row = StooqPrice {@@ -84,12 +85,12 @@ -- | Sends a request for multiple tickers at once. -- The function makes only a single HTTP call.-fetchPrices :: [StooqSymbol] -> IO (Maybe [StooqPrice])+fetchPrices :: [StooqSymbol] -> IO (Either String [StooqPrice]) fetchPrices tickers = fetchPrice (concatTickers tickers) where concatTickers :: [StooqSymbol] -> StooqSymbol concatTickers = foldl1 (\(StooqSymbol t1) (StooqSymbol t2) -> StooqSymbol (t1 ++ " " ++ t2)) -- | A shorthand around "fetchPrice" that allows to call the function using a plain String, without converting it to a `StooqSymbol` first.-fetch :: String -> IO (Maybe [StooqPrice])+fetch :: String -> IO (Either String [StooqPrice]) fetch = fetchPrice . StooqSymbol
src/utils/Web/Data/Stooq/Internals.hs view
@@ -1,14 +1,16 @@ {-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleInstances #-} module Web.Data.Stooq.Internals where -import Data.Aeson (FromJSON, decode)-import Data.ByteString.Lazy (ByteString, empty)-import Data.ByteString.Char8 (pack)-import Data.ByteString.Lazy.Search (replace)+import Data.ByteString.Lazy (ByteString)+import Data.Maybe (fromMaybe, catMaybes) import Data.Text (Text) import GHC.Generics (Generic) +import Data.Csv (decode, HasHeader(NoHeader), FromField, parseField, runParser)+import qualified Data.Vector as V+ data StooqRow = StooqRow { symbol :: !Text,@@ -21,16 +23,38 @@ volume :: Int } deriving (Show, Generic) -data StooqResponse =+newtype StooqResponse = StooqResponse { symbols :: [StooqRow] } deriving (Show, Generic) -instance FromJSON StooqRow-instance FromJSON StooqResponse+data TryField a = FieldValue a | NoValue -parseResponse :: ByteString -> Maybe StooqResponse-parseResponse = decode . replace searchedSubstring replacement+instance FromField (TryField Text) where+ parseField s = case runParser (parseField s) of+ Left err -> pure NoValue+ Right v -> pure $ FieldValue v++instance FromField (TryField Double) where+ parseField s = case runParser (parseField s) of+ Left err -> pure NoValue+ Right v -> pure $ FieldValue v++instance FromField (TryField Int) where+ parseField s = case runParser (parseField s) of+ Left err -> pure NoValue+ Right v -> pure $ FieldValue v++parseResponse :: ByteString -> Either String StooqResponse+parseResponse input =+ fmap (StooqResponse . catMaybes . V.toList . V.map tupleToStooqRow) (decode NoHeader input)+ where- searchedSubstring = pack ",\"openint\":}"- replacement = pack "}"+ tupleToStooqRow :: (Text, TryField Int, TryField Text, TryField Double, TryField Double, TryField Double, TryField Double, TryField Int, Text) -> Maybe StooqRow+ tupleToStooqRow (name, FieldValue date, FieldValue time, FieldValue open, FieldValue high, FieldValue low, FieldValue close, volume, _) =+ Just $ StooqRow name date time open high low close (defaultToZero volume)+ tupleToStooqRow (name, _, _, _, _, _, _, _, _) = Nothing++ defaultToZero :: TryField Int -> Int+ defaultToZero (FieldValue x) = x+ defaultToZero NoValue = 0
stooq-api.cabal view
@@ -1,7 +1,7 @@ cabal-version: 2.0 name: stooq-api-version: 0.3.1.0+version: 0.4.1.0 synopsis: A simple wrapper around stooq.pl API for downloading market data. description: Here's a simple wrapper around API offered by Stooq.pl.@@ -24,11 +24,11 @@ library stooq-api-utils exposed-modules: Web.Data.Stooq.Internals build-depends: base,- aeson, bytestring,- stringsearch,+ cassava, text,- time+ time,+ vector hs-source-dirs: src/utils default-language: Haskell2010 @@ -36,14 +36,13 @@ exposed-modules: Web.Data.Stooq.API build-depends: base >= 4.13 && < 4.16, bytestring >= 0.10.10 && < 0.11,- aeson >= 2.0 && < 2.1,+ cassava >= 0.5.2 && < 0.5.3, lens >= 4.18.1 && < 5.1, text >= 1.2 && < 1.3, time >= 1.9.3 && < 1.10, utf8-string >= 1.0.2 && < 1.1, vector >= 0.12.1 && < 0.13, wreq >= 0.5.3 && < 0.6,- stringsearch >= 0.3 && < 0.4, stooq-api-utils hs-source-dirs: src/lib default-language: Haskell2010