Execution layer for algorithmic trading
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.

142 lines
4.4 KiB

{-# LANGUAGE FlexibleContexts #-}
7 years ago
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
7 years ago
module ATrade.Quotes.QHP (
Period(..),
RequestParams(..),
QHPHandle,
mkQHPHandle,
requestHistoryFromQHP
7 years ago
) where
7 years ago
import ATrade.Exceptions
import ATrade.Logging (Message, logInfo, logDebug)
7 years ago
import ATrade.Types
import Colog (WithLog)
import Control.Exception.Safe (MonadThrow, throw)
import Control.Monad.IO.Class (MonadIO, liftIO)
7 years ago
import Data.Aeson
import Data.Binary.Get
import qualified Data.ByteString.Lazy as BL
import qualified Data.Text as T
7 years ago
import Data.Time.Calendar
import Data.Time.Clock
7 years ago
import Data.Time.Clock.POSIX
import Data.Time.Format
import Language.Haskell.Printf (t)
7 years ago
import System.ZMQ4
7 years ago
data Period =
Period1Min |
Period5Min |
Period15Min |
Period30Min |
PeriodHour |
PeriodDay |
PeriodWeek |
PeriodMonth
deriving (Eq)
instance Show Period where
7 years ago
show Period1Min = "M1"
show Period5Min = "M5"
7 years ago
show Period15Min = "M15"
show Period30Min = "M30"
7 years ago
show PeriodHour = "H1"
show PeriodDay = "D"
show PeriodWeek = "W"
7 years ago
show PeriodMonth = "MN"
data QHPHandle = QHPHandle
{
qhpContext :: Context
, qhpEndpoint :: T.Text
}
mkQHPHandle :: Context -> T.Text -> QHPHandle
mkQHPHandle = QHPHandle
requestHistoryFromQHP :: (WithLog env Message m, MonadThrow m, MonadIO m) => QHPHandle -> TickerId -> BarTimeframe -> UTCTime -> UTCTime -> m [Bar]
requestHistoryFromQHP qhp tickerId timeframe fromTime toTime =
case parseQHPPeriod (unBarTimeframe timeframe) of
Just tf -> getQuotes (qhpContext qhp) (params tf)
_ -> throw $ BadParams "QHP: Unable to parse timeframe"
where
params tf = RequestParams
{
endpoint = qhpEndpoint qhp,
ticker = tickerId,
startDate = utctDay fromTime,
endDate = utctDay toTime,
period = tf
}
parseQHPPeriod x
| x == 60 = Just Period1Min
| x == 5 * 60 = Just Period5Min
| x == 15 * 60 = Just Period15Min
| x == 30 * 60 = Just Period30Min
| x == 60 * 60 = Just PeriodHour
| x == 24 * 60 * 60 = Just PeriodDay
| otherwise = Nothing
7 years ago
data RequestParams =
RequestParams
{
7 years ago
endpoint :: T.Text,
ticker :: T.Text,
7 years ago
startDate :: Day,
7 years ago
endDate :: Day,
period :: Period
7 years ago
} deriving (Show, Eq)
7 years ago
printDatetime :: UTCTime -> String
printDatetime = formatTime defaultTimeLocale (iso8601DateFormat (Just "%H:%M:%S"))
7 years ago
instance ToJSON RequestParams where
toJSON p = object [ "ticker" .= ticker p,
"from" .= printDatetime (UTCTime (startDate p) 0),
"to" .= printDatetime (UTCTime (endDate p) 0),
7 years ago
"timeframe" .= show (period p) ]
7 years ago
getQuotes :: (WithLog env Message m, MonadIO m) => Context -> RequestParams -> m [Bar]
getQuotes ctx params = do
logInfo "QHP" $ "Connecting to ep: " <> endpoint params
logDebug "QHP" $ "From: " <> (T.pack . show) (startDate params) <> "; To: " <> (T.pack . show) (endDate params)
result <- liftIO $ withSocket ctx Req $ \sock -> do
7 years ago
connect sock $ (T.unpack . endpoint) params
send sock [] (BL.toStrict $ encode params)
response <- receiveMulti sock
case response of
[header, rest] -> if header == "OK"
then return $ reverse $ parseBars (ticker params) $ BL.fromStrict rest
else return []
_ -> return []
logInfo "QHP" $ "Obtained bars: " <> (T.pack . show . length) result
return result
7 years ago
parseBars :: TickerId -> BL.ByteString -> [Bar]
parseBars tickerId input =
case runGetOrFail parseBar input of
7 years ago
Left _ -> []
7 years ago
Right (rest, _, bar) -> bar : parseBars tickerId rest
where
parseBar = do
rawTimestamp <- realToFrac <$> getWord64le
baropen <- getDoublele
barhigh <- getDoublele
barlow <- getDoublele
barclose <- getDoublele
barvolume <- getWord64le
return Bar
{
barSecurity = tickerId,
barTimestamp = posixSecondsToUTCTime rawTimestamp,
barOpen = fromDouble baropen,
barHigh = fromDouble barhigh,
barLow = fromDouble barlow,
barClose = fromDouble barclose,
barVolume = toInteger barvolume
}