packages feed

Finance-Quote-Yahoo 0.7.0 → 0.8.0

raw patch · 3 files changed

+121/−115 lines, 3 filesdep +bytestringdep +http-conduitdep −HTTPdep −containersdep ~basedep ~time

Dependencies added: bytestring, http-conduit

Dependencies removed: HTTP, containers

Dependency ranges changed: base, time

Files

Finance-Quote-Yahoo.cabal view
@@ -1,5 +1,5 @@ Name:                Finance-Quote-Yahoo-Version:             0.7.0+Version:             0.8.0 Description:         Obtain quote data from finance.yahoo.com Synopsis:	     Obtain quote data from finance.yahoo.com Category:            Web@@ -9,14 +9,14 @@ License-file:        LICENSE Author:              brad clawsie Maintainer:          haskell@fastmail.fm-cabal-version: 	     >=1.2+cabal-version: 	     >=1.6 build-type:          Simple-Flag splitBase-  Description: Choose the new smaller, split-up base package.+ Library -  if flag(splitBase)-    Build-Depends: base >= 3,containers,old-locale-  else-    Build-Depends: base < 3-  Build-Depends: network,time>=1.1.1,HTTP>=4000.0.5+  Build-Depends: base >= 3 && < 5,bytestring,http-conduit,network,time,old-locale   Exposed-modules:     Finance.Quote.Yahoo++source-repository this+  type:             git+  location:         https://github.com/bradclawsie/haskell-Finance.Quote.Yahoo+  tag:              0.8
Finance/Quote/Yahoo.hs view
@@ -1,10 +1,10 @@ {- | Finance.Quote.Yahoo -Finance.Quote.Yahoo is a module to obtain quote information from -finance.yahoo.com, which delivers a csv file with data for various fields, +Finance.Quote.Yahoo is a module to obtain quote information from+finance.yahoo.com, which delivers a csv file with data for various fields, which are documented at http:\/\/www.gummy-stuff.org\/Yahoo-data.htm. -The homepage for this module is +The homepage for this module is http:\/\/www.b7j0c.org\/stuff\/haskell-yquote.xhtml  The license for this module is at@@ -17,18 +17,18 @@ signals, so the error can be noted and fixed. An example of this would be putting the start and end data in the wrong order for the retrieval of historical quotes or the creation of a malformed URI. On the other hand,-I continue to propogate Nothing() for networking issues as there may be -external issues creating these errors for which one may want program -execution to continue. My personal tendency is to fail early when +I continue to propogate Nothing() for networking issues as there may be+external issues creating these errors for which one may want program+execution to continue. My personal tendency is to fail early when possible and practical.   Exported functions: -getQuote, which takes a list of quote symbols (in the finance sense of -\"symbol\" - YHOO,GOOG etc), a list of fields, and -returns a Data.Map, where the keys are pairs (symbol,field) and -values are the returned Strings. Upon any problem, Nothing is +getQuote, which takes a list of quote symbols (in the finance sense of+\"symbol\" - YHOO,GOOG etc), a list of fields, and+returns a Data.Map, where the keys are pairs (symbol,field) and+values are the returned Strings. Upon any problem, Nothing is returned. I have not cast the data into stronger types than String since Yahoo is inconsistent about what is returned in the csv. Fields often contain punctuation, symbols, as well as numbers. So really, they are@@ -49,7 +49,7 @@   import Finance\.Quote\.Yahoo   import Data\.Time\.Calendar   import Data\.Map-  quoteSymbolList = [\"YHOO\",\"^DJI\"] :: [QuoteSymbol]+  quoteSymbolList = [\"YHOO\"] :: [QuoteSymbol]   quoteFieldsList = [\"s\",\"l1\",\"c\"] :: [QuoteField]   main = do   q <- getQuote quoteSymbolList quoteFieldsList@@ -73,19 +73,21 @@                             QuoteField,QuoteSymbol,QuoteValue,Quote,                             QuoteCurrency,QuoteFrequency(..),                             HistoricalQuote(..)) where-import qualified Network.HTTP as H (simpleHTTP,getResponseBody,getRequest) -import qualified Network.URI as U (parseURI,escapeURIString,-                                   isUnescapedInURI) +import qualified Data.ByteString.Lazy.Char8 as C+import qualified Network.HTTP.Conduit as H (simpleHttp)+import qualified Network.URI as U (escapeURIString,isUnescapedInURI) import qualified Data.Time.Calendar as T (Day(..),fromGregorian) import qualified Data.Time.Format as F (formatTime) import qualified System.Locale as L (defaultTimeLocale)-import qualified Data.Map as M (fromList,Map(..))+import qualified Data.Map as M (fromList,Map) import qualified Data.List as D (intersperse) -{- +import Debug.Trace++  {-   License info: -  The license is a simple BSD3-style license available here:  +  The license is a simple BSD3-style license available here:   http://www.b7j0c.org/content/license.txt  -}@@ -93,45 +95,47 @@ type QuoteField  = String type QuoteSymbol = String type QuoteValue  = String-type QuoteCSV = String+type QuoteCSV = C.ByteString type Quote = [(QuoteField,QuoteValue)] --- | This is the base uri to get csv quotes. Exported. -baseQuoteURI = "http://download.finance.yahoo.com/d/quotes.csv" :: String+-- | This is the base uri to get csv quotes. Exported.+baseQuoteURI :: String+baseQuoteURI = "http://download.finance.yahoo.com/d/quotes.csv"  -- | If you just want the name, latest price and change, use this. Exported.-defaultQuoteFields = ["n","l1","c"] :: [QuoteField] +defaultQuoteFields :: [QuoteField]+defaultQuoteFields = ["n","l1","c"]  -- | quoteReq will build a String representation of a Yahoo Finance CSV--- request URI. +-- request URI. quoteReq :: [QuoteSymbol] -> [QuoteField] -> String-quoteReq symbols fields = -    U.escapeURIString U.isUnescapedInURI -         $ baseQuoteURI ++ "?s=" ++ -         (join "+" symbols) ++ "&f=" ++ (concat fields)-             where -               join :: String -> [String] -> String-               join sep = concat . D.intersperse sep+quoteReq symbols fields =+  U.escapeURIString U.isUnescapedInURI+  $ baseQuoteURI ++ "?s=" +++  (join "+" symbols) ++ "&f=" ++ (concat fields)+  where+    join :: String -> [String] -> String+    join sep = concat . D.intersperse sep --- | parseQuote will take a list of symbols, a list of fields, and the +-- | parseQuote will take a list of symbols, a list of fields, and the -- csv data and return a Data.Map, as described below (see getQuote). -- If there is a mismatch in the number of fields being zipped to -- produce the map, an error is triggered. parseQuote :: [QuoteSymbol] -> [QuoteField] -> QuoteCSV ->               Maybe (M.Map (QuoteSymbol, QuoteField) QuoteValue)-parseQuote symbols fields csv = -    let l = concat $ map (split ',') $ lines $ -            filter (\c -> notElem c "\r\"") csv-        p = [(x,y) | x <- symbols, y <- fields] in-    case (length p == length l) of-      True -> Just (M.fromList $ zip p l)-      False -> error("mismatch in fields and returned data")+parseQuote symbols fields csv =+  let l = concatMap (csplit ',') $ C.lines $ +          C.filter (\c -> notElem c "\r\"") csv+      p = [(x,y) | x <- symbols, y <- fields] in+  case length p == length l of+    True -> Just (M.fromList $ zip p $ map C.unpack l)+    False -> error "mismatch in fields and returned data" --- | getQuote will take a list of symbols, a list of fields, and will +-- | getQuote will take a list of symbols, a list of fields, and will -- return a Data.Map, where the key type is--- (symbol,field) +-- (symbol,field) -- and the value type is whatever quote value string is returned.--- An example map entry: +-- An example map entry: -- -- key: \(\"YHOO\",\"c\"\), value: \"24.00\" --@@ -145,13 +149,10 @@ getQuote :: [QuoteSymbol] -> [QuoteField] ->             IO (Maybe (M.Map (QuoteSymbol, QuoteField) QuoteValue)) getQuote symbols fields =-    do-      let req = quoteReq symbols fields-      rsp <- H.simpleHTTP (H.getRequest req)-      csv <- H.getResponseBody rsp-      return $ parseQuote symbols fields csv+  fmap (parseQuote symbols fields) $ H.simpleHttp (quoteReq symbols fields)  -- | This is the base uri to get csv historical quote data. Exported.+baseHistoricalURI :: String baseHistoricalURI = "http://ichart.finance.yahoo.com/table.csv"  -- | Float is not an fully appropriate currency type, beware. Exported.@@ -160,25 +161,24 @@ -- | HistoricalQuote reflects the row form of a yahoo historical quote: -- Date,Open,High,Low,Close,Volume,Adj Close (taken from the csv itself). -- Exported.-data HistoricalQuote = -    HistoricalQuote { -      symbol :: QuoteSymbol,-      date :: T.Day,-      open :: QuoteCurrency,-      high :: QuoteCurrency,-      low :: QuoteCurrency,-      close :: QuoteCurrency,-      adjclose :: QuoteCurrency,-      volume :: Int-    } deriving Show-+data HistoricalQuote =+      HistoricalQuote {+        symbol :: QuoteSymbol,+        date :: T.Day,+        open :: QuoteCurrency,+        high :: QuoteCurrency,+        low :: QuoteCurrency,+        close :: QuoteCurrency,+        adjclose :: QuoteCurrency,+        volume :: Int+        } deriving Show+                    -- | QuoteFrequency - frequency for historical quotes. Exported. data QuoteFrequency = Daily | Weekly | Monthly | Dividend---- | historicalQuoteReq will build a String representation of a --- Yahoo Finance CSV historical quote request URI. -historicalQuoteReq :: QuoteSymbol -> T.Day -> T.Day -> QuoteFrequency ->-                      String+                                                 +-- | historicalQuoteReq will build a String representation of a+-- Yahoo Finance CSV historical quote request URI.+historicalQuoteReq :: QuoteSymbol -> T.Day -> T.Day -> QuoteFrequency -> String historicalQuoteReq symbol start end freq =      let (startDay,startMonth,startYear) = dateArgs start         (endDay,endMonth,endYear) = dateArgs end @@ -190,7 +190,7 @@            "&g=" ++ [freqChar]     where       -- Return the string args for the URI.-      -- Note when parsing months - for some odd reason Yahoo has decided +      -- Note when parsing months - for some odd reason Yahoo has decided       -- that month numbers should be zero-based...06 = July, etc.       dateArgs :: T.Day -> (String,String,String)       dateArgs t = (day,month,year) where @@ -207,34 +207,35 @@                     Monthly -> 'm'                     Dividend -> 'v' --- | parseHistorical takes the raw csv from Yahoo Finance and returns +-- | parseHistorical takes the raw csv from Yahoo Finance and returns -- a list of HistoricalQuote entries. parseHistorical :: QuoteSymbol -> QuoteCSV -> Maybe [HistoricalQuote]-parseHistorical symbol' csv = -    let l = reverse $ map (split ',') $-            (tail . lines) $ filter (\c -> notElem c "\r") csv in-        Just $ map (makeHistoricalQuote) l-    where-      -- Create a HistoricalQuote entry from a line from the csv.-      makeHistoricalQuote :: [String] -> HistoricalQuote-      makeHistoricalQuote l = -          case (length l == 7) of-            False -> error("malformed line:" ++ (show l))-            True -> let date'     = makeDay (l!!0)-                        open'     = read (l!!1) :: QuoteCurrency-                        high'     = read (l!!2) :: QuoteCurrency-                        low'      = read (l!!3) :: QuoteCurrency-                        close'    = read (l!!4) :: QuoteCurrency-                        adjclose' = read (l!!6) :: QuoteCurrency-                        volume'   = read (l!!5) :: Int                 -                    in HistoricalQuote { symbol   = symbol',-                                         date     = date',-                                         open     = open',-                                         high     = high',-                                         low      = low',-                                         close    = close',-                                         adjclose = adjclose',-                                         volume   = volume' }+parseHistorical symbol' csv =+  let l = reverse $ map (csplit ',') $+          (tail . C.lines) $ C.filter (\c -> notElem c "\r") csv in+  Just $ map (makeHistoricalQuote . map C.unpack) l+  where+    -- Create a HistoricalQuote entry from a line from the csv.+    makeHistoricalQuote :: [String] -> HistoricalQuote+    makeHistoricalQuote l = +      case (length l == 7) of+        False -> error("malformed line:" ++ (show l))+        True -> let+          date'     = makeDay (l!!0)+          open'     = read (l!!1) :: QuoteCurrency+          high'     = read (l!!2) :: QuoteCurrency+          low'      = read (l!!3) :: QuoteCurrency+          close'    = read (l!!4) :: QuoteCurrency+          adjclose' = read (l!!6) :: QuoteCurrency+          volume'   = read (l!!5) :: Int                 +          in HistoricalQuote { symbol   = symbol',+                               date     = date',+                               open     = open',+                               high     = high',+                               low      = low',+                               close    = close',+                               adjclose = adjclose',+                               volume   = volume' }           where             -- Create a Day type from the str date from the csv.             makeDay :: String -> T.Day@@ -248,27 +249,27 @@                           T.fromGregorian y m d  -- | getHistoricalQuote takes a stock symbol, start and end date ranges,--- a quote frequency setting, and obtains the HistoricalQuote lines --- for this given date range and quote frequency. --- Supported frequencies are "Daily", "Weekly", +-- a quote frequency setting, and obtains the HistoricalQuote lines+-- for this given date range and quote frequency.+-- Supported frequencies are "Daily", "Weekly", -- "Monthly" or "Dividend". Hopefully these are self-explanatory. -- Nothing is returned on any error, but note if you ask for the quotes -- based on Dividend frequency for a stock that pays no dividends, you -- will not see Nothing, but just an empty result. -- Check finance.yahoo.com to see how -- far they offer quote history for a symbol you are interested in.--- Note! Yahoo takes some liberties with dates due to weekends and +-- Note! Yahoo takes some liberties with dates due to weekends and -- holidays and market closures. Exported. -- -- Here is what a sample result looks like for one day in the history: -- -- HistoricalQuote \{symbol \= \"YHOO\",---                   date \= 2007-07-02\, ---                   open \= 27.19\, ---                   high \= 27.27\, ---                   low \= 26.76\, ---                   close \= 26.86\, ---                   adjclose \= 26.86\, +--                   date \= 2007-07-02\,+--                   open \= 27.19\,+--                   high \= 27.27\,+--                   low \= 26.76\,+--                   close \= 26.86\,+--                   adjclose \= 26.86\, --                   volume \= 21011000\} -- getHistoricalQuote :: QuoteSymbol -> T.Day -> T.Day -> QuoteFrequency -> @@ -278,13 +279,19 @@       False -> error("start date must be earlier than end date")       True -> do          let req = historicalQuoteReq symbol start end freq-        rsp <- H.simpleHTTP (H.getRequest req)-        csv <- H.getResponseBody rsp+        csv <- H.simpleHttp req         return $ parseHistorical symbol csv  -- split copied from missingh+csplit :: Char -> C.ByteString -> [C.ByteString]+csplit delim s = if C.null rest+                 then [token]+                 else token : csplit delim (C.tail rest)+  where (token,rest) = C.span (/=delim) s+        +-- split copied from missingh split :: Char -> String -> [String]-split delim s = if null rest then [token]-                             else token : split delim (tail rest)+split delim s = if null rest+                then [token]+                else token : split delim (tail rest)   where (token,rest) = span (/=delim) s-
LICENSE view
@@ -1,5 +1,4 @@-Copyright (c) 2007, Brad Clawsie-All rights reserved.+Copyright (c) 2007-2013, Brad Clawsie All rights reserved.  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: