packages feed

iexcloud-0.0.0.1: src/Net/Stocks.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE TemplateHaskell #-}

module Net.Stocks
       (
         getChart,
         getChart5y,
         getChart2y,
         getChart1y,
         getChart6m,
         getChart3m,
         getChart1m,
         getCompany,
         getBook,
         getDelayedQuote,
         getDelayedDividend,
         getEarnings,
         getEffectiveSpread,
         getFinancials,
         getStats,
         getNewsItem,
         getOHLC,
         getPrevious,
         getPeers,
         getPrice,
         getQuote,
         getRelevant,
         getSplit,
         getVolumeByVenue,
         getTS,
         getBatch,
         getBatchCompany,
         getMarket,
         getIntraDayStats,
         getRecentStats,
         getRecordStats,
         getHistoricalStats,
         -- getHistoricalDailyStats,
         typeQuery,
         getChart',
         getChart5y',
         getChart2y',
         getChart1y',
         getChart6m',
         getChart3m',
         getChart1m',
         getCompany',
         getBook',
         getDelayedQuote',
         getDelayedDividend',
         getEarnings',
         getEffectiveSpread',
         getFinancials',
         getStats',
         getNewsItem',
         getOHLC',
         getPrevious',
         getPeers',
         getPrice',
         getQuote',
         getRelevant',
         getSplit',
         getVolumeByVenue',
         getTS',
         Batch (..),
         BatchQuery (..)
       ) where

import System.IO
import GHC.Generics
import Control.Exception
import Data.Aeson
import Data.Char
import Data.HashMap.Strict
import Data.Maybe
import Network.HTTP.Conduit

import qualified Data.ByteString.Lazy.Char8 as L8
import qualified Data.List                  as DL
import qualified Data.Map                   as DM

import qualified Net.IEX.Chart              as IEXChart
import qualified Net.IEX.Company            as IEXCompany
import qualified Net.IEX.Stats              as IEXStats
import qualified Net.IEX.Earnings           as IEXEarnings
import qualified Net.IEX.NewsItem           as IEXNewsItem
import qualified Net.IEX.DelayedQuote       as IEXDelayedQuote
import qualified Net.IEX.Dividend           as IEXDividend
import qualified Net.IEX.EffectiveSpread    as IEXEffectiveSpread
import qualified Net.IEX.Financials         as IEXFinancials
import qualified Net.IEX.OHLC               as IEXOHLC
import qualified Net.IEX.PriceTime          as IEXPriceTime
import qualified Net.IEX.Previous           as IEXPrevious
import qualified Net.IEX.Quote              as IEXQuote
import qualified Net.IEX.Split              as IEXSplit
import qualified Net.IEX.VolumeByVenue      as IEXVolumeByVenue
import qualified Net.IEX.Relevant           as IEXRelevant
import qualified Net.IEX.Market             as IEXMarket
import qualified Net.IEX.IntraDayStats      as IEXIntraDayStats
import qualified Net.IEX.RecentStats        as IEXRecentStats
import qualified Net.IEX.RecordStats        as IEXRecordStats
import qualified Net.IEX.Book               as IEXBook
import qualified Net.IEX.TimeSeries         as IEXTimeSeries

type Symbol = String
type AuthAndSymbol = (String, Symbol)

data BatchQuery =  NewsQuery            |
                   ChartQuery           |
                   CompanyQuery         |
                   DelayedQuoteQuery    |
                   DividendQuery        |
                   EarningsQuery        |
                   EffectiveSpreadQuery |
                   FinancialsQuery      |
                   StatsQuery           |
                   OHLCQuery            |
                   PriceTimeQuery       |
                   PreviousQuery        |
                   QuoteQuery           |
                   SplitQuery           |
                   VolumeByVenueQuery

batchQueryToStr :: BatchQuery -> String
batchQueryToStr NewsQuery = "news"
batchQueryToStr ChartQuery = "chart"
batchQueryToStr CompanyQuery = "company"
batchQueryToStr DelayedQuoteQuery = "delayedquote"
batchQueryToStr DividendQuery = "dividends"
batchQueryToStr EarningsQuery = "earnings"
batchQueryToStr EffectiveSpreadQuery = "effectivespread"
batchQueryToStr FinancialsQuery = "financials"
batchQueryToStr StatsQuery = "stats"
batchQueryToStr OHLCQuery = "ohlc"
batchQueryToStr PriceTimeQuery = "price"
batchQueryToStr QuoteQuery = "quote"
batchQueryToStr SplitQuery = "split"
batchQueryToStr VolumeByVenueQuery = "volumebyvenue"

data Batch = Batch {
  news :: Maybe [IEXNewsItem.NewsItem],
  chart :: Maybe [IEXChart.Chart],
  company :: Maybe IEXCompany.Company,
  delayedQuote :: Maybe IEXDelayedQuote.DelayedQuote,
  dividend :: Maybe [IEXDividend.Dividend],
  earnings :: Maybe IEXEarnings.Earnings,
  effectiveSpread :: Maybe [IEXEffectiveSpread.EffectiveSpread],
  financials :: Maybe IEXFinancials.Financials,
  stats :: Maybe IEXStats.Stats,
  ohlc :: Maybe IEXOHLC.OHLC,
  priceTime :: Maybe Integer,
  previous :: Maybe IEXPrevious.Previous,
  quote :: Maybe IEXQuote.Quote,
  split :: Maybe [IEXSplit.Split],
  volumeByVenue :: Maybe [IEXVolumeByVenue.VolumeByVenue]
} deriving (Generic, Show, Eq)

-- ToJSON means taking a haskell data structure and making a JSON string
instance ToJSON Batch

-- FromJSON means parsing the text into a haskell data structure
instance FromJSON Batch

baseURL :: String
baseURL = "https://cloud.iexapis.com/stable/stock/"

sandboxURL :: String
sandboxURL = "https://sandbox.iexapis.com/stable/stock/"

marketURL :: String
marketURL = "https://api.iextrading.com/1.0/market"

intraDayURL :: String
intraDayURL = "https://api.iextrading.com/1.0/stats/intraday"

statsURL :: String
statsURL = "https://api.iextrading.com/1.0/stats/"

lowerString :: Symbol -> String
lowerString = DL.map toLower

tokenize :: String -> String
tokenize auth = "?token=" ++ auth

getChart :: AuthAndSymbol -> IO (Maybe [IEXChart.Chart])
getChart (auth, symb) = do
  obj <- getNonJSONData (baseURL ++ lowerString symb ++ "/chart" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right str ->
      return $ decode str

getChart5y :: AuthAndSymbol -> IO (Maybe [IEXChart.Chart])
getChart5y (auth, symb) = do
  obj <- getNonJSONData (baseURL ++ lowerString symb ++ "/chart/5y" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right str ->
      return $ decode str

getChart2y :: AuthAndSymbol -> IO (Maybe [IEXChart.Chart])
getChart2y (auth, symb) = do
  obj <- getNonJSONData (baseURL ++ lowerString symb ++ "/chart/2y" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right str ->
      return $ decode str

getChart1y :: AuthAndSymbol -> IO (Maybe [IEXChart.Chart])
getChart1y (auth, symb) = do
  obj <- getNonJSONData (baseURL ++ lowerString symb ++ "/chart/1y" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right str ->
      return $ decode str

getChart6m :: AuthAndSymbol -> IO (Maybe [IEXChart.Chart])
getChart6m (auth, symb) = do
  obj <- getNonJSONData (baseURL ++ lowerString symb ++ "/chart/6m" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right str ->
      return $ decode str

getChart3m :: AuthAndSymbol -> IO (Maybe [IEXChart.Chart])
getChart3m (auth, symb) = do
  obj <- getNonJSONData (baseURL ++ lowerString symb ++ "/chart/3m" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right str ->
      return $ decode str

getChart1m :: AuthAndSymbol -> IO (Maybe [IEXChart.Chart])
getChart1m (auth, symb) = do
  obj <- getNonJSONData (baseURL ++ lowerString symb ++ "/chart/1m" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right str ->
      return $ decode str

getCompany :: AuthAndSymbol -> IO (Maybe IEXCompany.Company)
getCompany (auth, symb) = do
  obj <- getNonJSONData (baseURL ++ lowerString symb ++ "/company" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right bytestr ->
      return $ decode bytestr

getBook :: AuthAndSymbol -> IO (Maybe IEXBook.Book)
getBook (auth, symb) = do
  obj <- getNonJSONData (baseURL ++ lowerString symb ++ "/book" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right bytestr ->
      return $ decode bytestr

getDelayedQuote :: AuthAndSymbol -> IO (Maybe IEXDelayedQuote.DelayedQuote)
getDelayedQuote (auth, symb) = do
  obj <- getNonJSONData (baseURL ++ lowerString symb ++ "/delayed-quote" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right bytestr ->
      return $ decode bytestr

getDelayedDividend :: AuthAndSymbol -> IO (Maybe [IEXDividend.Dividend])
getDelayedDividend (auth, symb) = do
  obj <- getNonJSONData (baseURL ++ lowerString symb ++ "/dividends/5y" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right bytestr ->
      return $ decode bytestr

getEarnings :: AuthAndSymbol -> IO (Maybe IEXEarnings.Earnings)
getEarnings (auth, symb) = do
  obj <- getNonJSONData (baseURL ++ lowerString symb ++ "/earnings" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right bytestr ->
      return $ decode bytestr

getEffectiveSpread :: AuthAndSymbol -> IO (Maybe [IEXEffectiveSpread.EffectiveSpread])
getEffectiveSpread (auth, symb) = do
  obj <- getNonJSONData (baseURL ++ lowerString symb ++ "/effective-spread" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right bytestr ->
      return $ decode bytestr

getFinancials :: AuthAndSymbol -> IO (Maybe IEXFinancials.Financials)
getFinancials (auth, symb) = do
  obj <- getNonJSONData (baseURL ++ lowerString symb ++ "/financials" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right bytestr ->
      return $ decode bytestr

getStats :: AuthAndSymbol -> IO (Maybe IEXStats.Stats)
getStats (auth, symb) = do
  obj <- getNonJSONData (baseURL ++ lowerString symb ++ "/stats" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right bytestr ->
      return $ decode bytestr

getNewsItem :: AuthAndSymbol -> IO (Maybe [IEXNewsItem.NewsItem])
getNewsItem (auth, symb) = do
  obj <- getNonJSONData (baseURL ++ lowerString symb ++ "/news/last/1" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right bytestr ->
      return $ decode bytestr

getOHLC :: AuthAndSymbol -> IO (Maybe IEXOHLC.OHLC)
getOHLC (auth, symb) = do
  obj <- getNonJSONData (baseURL ++ lowerString symb ++ "/ohlc" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right bytestr ->
      return $ decode bytestr

getPeers :: AuthAndSymbol -> IO (Maybe L8.ByteString)
getPeers (auth, symb) = do
  obj <- getNonJSONData (baseURL ++ lowerString symb ++ "/peers" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right bytestr ->
      return $ Just bytestr

getPrevious :: AuthAndSymbol -> IO (Maybe IEXPrevious.Previous)
getPrevious (auth, symb) = do
  obj <- getNonJSONData (baseURL ++ lowerString symb ++ "/previous" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right bytestr ->
      return $ decode bytestr

-- FIXME: do not json parse an int
getPrice :: AuthAndSymbol -> IO (Maybe Double)
getPrice (auth, symb) = do
  obj <- getNonJSONData (baseURL ++ lowerString symb ++ "/price" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right bytestr ->
      return $ decode bytestr

getQuote :: AuthAndSymbol -> IO (Maybe IEXQuote.Quote)
getQuote (auth, symb) = do
  obj <- getNonJSONData (baseURL ++ lowerString symb ++ "/quote" ++ tokenize auth)
  case obj of
    Left _ -> do
      return Nothing
    Right bytestr ->
      return $ decode bytestr

getRelevant :: AuthAndSymbol -> IO (Maybe IEXRelevant.Relevant)
getRelevant (auth, symb) = do
  obj <- getNonJSONData (baseURL ++ lowerString symb ++ "/relevant" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right bytestr ->
      return $ decode bytestr

getSplit :: AuthAndSymbol -> IO (Maybe [IEXSplit.Split])
getSplit (auth, symb) = do
  obj <- getNonJSONData (baseURL ++ lowerString symb ++ "/splits/5y" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right bytestr ->
      return $ decode bytestr

getVolumeByVenue :: AuthAndSymbol -> IO (Maybe [IEXVolumeByVenue.VolumeByVenue])
getVolumeByVenue (auth, symb) = do
  obj <- getNonJSONData (baseURL ++ lowerString symb ++ "/volume-by-venue" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right bytestr ->
      return $ decode bytestr

getTS :: AuthAndSymbol -> IO (Maybe [IEXTimeSeries.TimeSeries])
getTS (auth, symb) = do
  obj <- getNonJSONData (baseURL ++ lowerString symb ++ "/time-series" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right bytestr ->
      return $ decode bytestr

-- get a list of parts we want in a batch request, and translate that
-- to HTTP parameters to provide to the API call
typeQuery :: [BatchQuery] -> String
typeQuery [] = ""
typeQuery inp =
  "types=" ++ (concat $ DL.intersperse "," (fmap batchQueryToStr inp))

symbolQuery :: [Symbol] -> String
symbolQuery [] = ""
symbolQuery inp = "symbols=" ++ (concat $ DL.intersperse "," inp)

questionMark :: [BatchQuery] -> String
questionMark [] = ""
questionMark _  = "?"

getBatch :: [Symbol] -> [BatchQuery] -> IO (Maybe (DM.Map String Batch))
getBatch symbs queryParams =
  let urlPt = baseURL ++ "market/batch?"
      fullQuery = urlPt ++ symbolQuery symbs ++ "&" ++ typeQuery queryParams
  in do
    obj <- getNonJSONData fullQuery
    case obj of
      Left _ ->
        return Nothing
      Right bytestr ->
        return $ decode bytestr

-- batch query of a *single* company
getBatchCompany :: Symbol -> [BatchQuery] -> IO (Maybe Batch)
getBatchCompany symb queryParams =
  let urlPt = (baseURL ++ lowerString symb ++ "/batch/")
      fullQuery = urlPt ++ (questionMark queryParams) ++ (typeQuery queryParams)
  in do
    obj <- getNonJSONData fullQuery
    case obj of
      Left _ ->
        return Nothing
      Right bytestr ->
        return $ decode bytestr

getMarket :: IO (Maybe [IEXMarket.Market])
getMarket = do
  obj <- getNonJSONData marketURL
  case obj of
    Left _ ->
      return Nothing
    Right bytestr ->
      return $ decode bytestr

getIntraDayStats :: IO (Maybe IEXIntraDayStats.IntraDayStats)
getIntraDayStats = do
    obj <- getNonJSONData intraDayURL
    case obj of
      Left _ ->
        return Nothing
      Right bytestr ->
        return $ decode bytestr

getRecentStats :: IO (Maybe [IEXRecentStats.RecentStats])
getRecentStats = do
    obj <- getNonJSONData (statsURL ++ "recent")
    case obj of
      Left _ ->
        return Nothing
      Right bytestr ->
        return $ decode bytestr

getRecordStats :: IO (Maybe IEXRecordStats.RecordStats)
getRecordStats = do
    obj <- getNonJSONData (statsURL ++ "records")
    case obj of
      Left _ ->
        return Nothing
      Right bytestr ->
        return $ decode bytestr

getHistoricalStats :: IO (Maybe [IEXRecentStats.RecentStats])
getHistoricalStats = undefined

-- currently does not work due to inconsitency in
-- IEX API. isHalfDay is Bool in one API call and Int in another

-- getHistoricalDailyStats :: IO (Maybe [IEXRecentStats.RecentStats])
-- getHistoricalDailyStats = do
--     obj <- getNonJSONData (statsURL ++ "historical/daily")
--     return $ decode obj

getNonJSONData :: String -> IO (Either SomeException L8.ByteString)
getNonJSONData query = do
  putStrLn query
  try $ simpleHttp query

-- | Functions to work with the sandbox
--
getChart' :: AuthAndSymbol -> IO (Maybe [IEXChart.Chart])
getChart' (auth, symb) = do
  obj <- getNonJSONData (sandboxURL ++ lowerString symb ++ "/chart" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right str ->
      return $ decode str

getChart5y' :: AuthAndSymbol -> IO (Maybe [IEXChart.Chart])
getChart5y' (auth, symb) = do
  obj <- getNonJSONData (sandboxURL ++ lowerString symb ++ "/chart/5y" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right str ->
      return $ decode str

getChart2y' :: AuthAndSymbol -> IO (Maybe [IEXChart.Chart])
getChart2y' (auth, symb) = do
  obj <- getNonJSONData (sandboxURL ++ lowerString symb ++ "/chart/2y" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right str ->
      return $ decode str

getChart1y' :: AuthAndSymbol -> IO (Maybe [IEXChart.Chart])
getChart1y' (auth, symb) = do
  obj <- getNonJSONData (sandboxURL ++ lowerString symb ++ "/chart/1y" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right str ->
      return $ decode str

getChart6m' :: AuthAndSymbol -> IO (Maybe [IEXChart.Chart])
getChart6m' (auth, symb) = do
  obj <- getNonJSONData (sandboxURL ++ lowerString symb ++ "/chart/6m" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right str ->
      return $ decode str

getChart3m' :: AuthAndSymbol -> IO (Maybe [IEXChart.Chart])
getChart3m' (auth, symb) = do
  obj <- getNonJSONData (sandboxURL ++ lowerString symb ++ "/chart/3m" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right str ->
      return $ decode str

getChart1m' :: AuthAndSymbol -> IO (Maybe [IEXChart.Chart])
getChart1m' (auth, symb) = do
  obj <- getNonJSONData (sandboxURL ++ lowerString symb ++ "/chart/1m" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right str ->
      return $ decode str

getCompany' :: AuthAndSymbol -> IO (Maybe IEXCompany.Company)
getCompany' (auth, symb) = do
  obj <- getNonJSONData (sandboxURL ++ lowerString symb ++ "/company" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right bytestr ->
      return $ decode bytestr

getBook' :: AuthAndSymbol -> IO (Maybe IEXBook.Book)
getBook' (auth, symb) = do
  obj <- getNonJSONData (sandboxURL ++ lowerString symb ++ "/book" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right bytestr ->
      return $ decode bytestr

getDelayedQuote' :: AuthAndSymbol -> IO (Maybe IEXDelayedQuote.DelayedQuote)
getDelayedQuote' (auth, symb) = do
  obj <- getNonJSONData (sandboxURL ++ lowerString symb ++ "/delayed-quote" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right bytestr ->
      return $ decode bytestr

getDelayedDividend' :: AuthAndSymbol -> IO (Maybe [IEXDividend.Dividend])
getDelayedDividend' (auth, symb) = do
  obj <- getNonJSONData (sandboxURL ++ lowerString symb ++ "/dividends/5y" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right bytestr ->
      return $ decode bytestr

getEarnings' :: AuthAndSymbol -> IO (Maybe IEXEarnings.Earnings)
getEarnings' (auth, symb) = do
  obj <- getNonJSONData (sandboxURL ++ lowerString symb ++ "/earnings" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right bytestr ->
      return $ decode bytestr

getEffectiveSpread' :: AuthAndSymbol -> IO (Maybe [IEXEffectiveSpread.EffectiveSpread])
getEffectiveSpread' (auth, symb) = do
  obj <- getNonJSONData (sandboxURL ++ lowerString symb ++ "/effective-spread" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right bytestr ->
      return $ decode bytestr

getFinancials' :: AuthAndSymbol -> IO (Maybe IEXFinancials.Financials)
getFinancials' (auth, symb) = do
  obj <- getNonJSONData (sandboxURL ++ lowerString symb ++ "/financials" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right bytestr ->
      return $ decode bytestr

getStats' :: AuthAndSymbol -> IO (Maybe IEXStats.Stats)
getStats' (auth, symb) = do
  obj <- getNonJSONData (sandboxURL ++ lowerString symb ++ "/stats" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right bytestr ->
      return $ decode bytestr

getNewsItem' :: AuthAndSymbol -> IO (Maybe [IEXNewsItem.NewsItem])
getNewsItem' (auth, symb) = do
  obj <- getNonJSONData (sandboxURL ++ lowerString symb ++ "/news/last/1" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right bytestr ->
      return $ decode bytestr

getOHLC' :: AuthAndSymbol -> IO (Maybe IEXOHLC.OHLC)
getOHLC' (auth, symb) = do
  obj <- getNonJSONData (sandboxURL ++ lowerString symb ++ "/ohlc" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right bytestr ->
      return $ decode bytestr

getPeers' :: AuthAndSymbol -> IO (Maybe L8.ByteString)
getPeers' (auth, symb) = do
  obj <- getNonJSONData (sandboxURL ++ lowerString symb ++ "/peers" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right bytestr ->
      return $ Just bytestr

getPrevious' :: AuthAndSymbol -> IO (Maybe IEXPrevious.Previous)
getPrevious' (auth, symb) = do
  obj <- getNonJSONData (sandboxURL ++ lowerString symb ++ "/previous" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right bytestr ->
      return $ decode bytestr

-- FIXME: do not json parse an int
getPrice' :: AuthAndSymbol -> IO (Maybe Double)
getPrice' (auth, symb) = do
  obj <- getNonJSONData (sandboxURL ++ lowerString symb ++ "/price" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right bytestr ->
      return $ decode bytestr

getQuote' :: AuthAndSymbol -> IO (Maybe IEXQuote.Quote)
getQuote' (auth, symb) = do
  obj <- getNonJSONData (sandboxURL ++ lowerString symb ++ "/quote" ++ tokenize auth)
  case obj of
    Left _ -> do
      return Nothing
    Right bytestr ->
      return $ decode bytestr

getRelevant' :: AuthAndSymbol -> IO (Maybe IEXRelevant.Relevant)
getRelevant' (auth, symb) = do
  obj <- getNonJSONData (sandboxURL ++ lowerString symb ++ "/relevant" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right bytestr ->
      return $ decode bytestr

getSplit' :: AuthAndSymbol -> IO (Maybe [IEXSplit.Split])
getSplit' (auth, symb) = do
  obj <- getNonJSONData (sandboxURL ++ lowerString symb ++ "/splits/5y" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right bytestr ->
      return $ decode bytestr

getVolumeByVenue' :: AuthAndSymbol -> IO (Maybe [IEXVolumeByVenue.VolumeByVenue])
getVolumeByVenue' (auth, symb) = do
  obj <- getNonJSONData (sandboxURL ++ lowerString symb ++ "/volume-by-venue" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right bytestr ->
      return $ decode bytestr

getTS' :: AuthAndSymbol -> IO (Maybe [IEXTimeSeries.TimeSeries])
getTS' (auth, symb) = do
  obj <- getNonJSONData (sandboxURL ++ lowerString symb ++ "/time-series" ++ tokenize auth)
  case obj of
    Left _ ->
      return Nothing
    Right bytestr ->
      return $ decode bytestr