|
|
|
|
@ -1,10 +1,16 @@
@@ -1,10 +1,16 @@
|
|
|
|
|
{-# LANGUAGE OverloadedStrings, TypeSynonymInstances, FlexibleInstances #-} |
|
|
|
|
{-# LANGUAGE DeriveGeneric #-} |
|
|
|
|
{-# LANGUAGE FlexibleInstances #-} |
|
|
|
|
{-# LANGUAGE OverloadedStrings #-} |
|
|
|
|
{-# LANGUAGE TypeSynonymInstances #-} |
|
|
|
|
|
|
|
|
|
module ATrade.Types ( |
|
|
|
|
TickerId, |
|
|
|
|
Tick(..), |
|
|
|
|
Bar(..), |
|
|
|
|
serializeBar, |
|
|
|
|
serializeBarBody, |
|
|
|
|
deserializeBar, |
|
|
|
|
BarTimeframe(..), |
|
|
|
|
DataType(..), |
|
|
|
|
serializeTick, |
|
|
|
|
serializeTickBody, |
|
|
|
|
@ -32,8 +38,8 @@ import ATrade.Price
@@ -32,8 +38,8 @@ import ATrade.Price
|
|
|
|
|
import Control.Monad |
|
|
|
|
import Data.Aeson |
|
|
|
|
import Data.Aeson.Types |
|
|
|
|
import Data.Binary.Builder |
|
|
|
|
import Data.Binary.Get |
|
|
|
|
import Data.Binary.Put |
|
|
|
|
import Data.ByteString.Lazy as B |
|
|
|
|
import Data.DateTime |
|
|
|
|
import Data.Int |
|
|
|
|
@ -96,18 +102,29 @@ data Tick = Tick {
@@ -96,18 +102,29 @@ data Tick = Tick {
|
|
|
|
|
volume :: !Integer |
|
|
|
|
} deriving (Show, Eq, Generic) |
|
|
|
|
|
|
|
|
|
putPrice :: Price -> Put |
|
|
|
|
putPrice price = do |
|
|
|
|
let (i, f) = decompose price |
|
|
|
|
putWord64le $ fromInteger . toInteger $ i |
|
|
|
|
putWord32le $ (* 1000) . fromInteger . toInteger $ f |
|
|
|
|
|
|
|
|
|
parsePrice :: Get Price |
|
|
|
|
parsePrice = do |
|
|
|
|
intpart <- (fromIntegral <$> getWord64le) :: Get Int64 |
|
|
|
|
nanopart <- (fromIntegral <$> getWord32le) :: Get Int32 |
|
|
|
|
return $ compose (intpart, nanopart `div` 1000) |
|
|
|
|
|
|
|
|
|
serializeTickHeader :: Tick -> ByteString |
|
|
|
|
serializeTickHeader tick = B.fromStrict . E.encodeUtf8 $ security tick |
|
|
|
|
|
|
|
|
|
serializeTickBody :: Tick -> ByteString |
|
|
|
|
serializeTickBody tick = toLazyByteString $ mconcat [ |
|
|
|
|
putWord32le 1, |
|
|
|
|
putWord64le $ fromIntegral . toSeconds' . timestamp $ tick, |
|
|
|
|
putWord32le $ fromIntegral . fracSeconds . timestamp $ tick, |
|
|
|
|
putWord32le $ fromIntegral . fromEnum . datatype $ tick, |
|
|
|
|
putWord64le $ fromInteger . toInteger . fst . decompose . value $ tick, |
|
|
|
|
putWord32le $ (* 1000) . fromInteger . toInteger . snd . decompose . value $ tick, |
|
|
|
|
putWord32le $ fromIntegral $ volume tick ] |
|
|
|
|
serializeTickBody tick = runPut $ do |
|
|
|
|
putWord32le 1 |
|
|
|
|
putWord64le $ fromIntegral . toSeconds' . timestamp $ tick |
|
|
|
|
putWord32le $ fromIntegral . fracSeconds . timestamp $ tick |
|
|
|
|
putWord32le $ fromIntegral . fromEnum . datatype $ tick |
|
|
|
|
putPrice $ value tick |
|
|
|
|
putWord32le $ fromIntegral $ volume tick |
|
|
|
|
where |
|
|
|
|
fractionalPart :: (RealFrac a) => a -> a |
|
|
|
|
fractionalPart x = x - fromIntegral (truncate x) |
|
|
|
|
@ -125,15 +142,14 @@ parseTick = do
@@ -125,15 +142,14 @@ parseTick = do
|
|
|
|
|
tsec <- getWord64le |
|
|
|
|
tusec <- getWord32le |
|
|
|
|
dt <- toEnum . fromEnum <$> getWord32le |
|
|
|
|
intpart <- (fromIntegral <$> getWord64le) :: Get Int64 |
|
|
|
|
nanopart <- (fromIntegral <$> getWord32le) :: Get Int32 |
|
|
|
|
price <- parsePrice |
|
|
|
|
volume <- fromIntegral <$> (fromIntegral <$> getWord32le :: Get Int32) |
|
|
|
|
return Tick { security = "", |
|
|
|
|
datatype = dt, |
|
|
|
|
timestamp = makeTimestamp tsec tusec, |
|
|
|
|
value = compose (intpart, nanopart `div` 1000), |
|
|
|
|
value = price, |
|
|
|
|
volume = volume } |
|
|
|
|
where |
|
|
|
|
|
|
|
|
|
makeTimestamp :: Word64 -> Word32 -> UTCTime |
|
|
|
|
makeTimestamp sec usec = addUTCTime (fromRational $ toInteger usec % 1000000) (fromSeconds . toInteger $ sec) |
|
|
|
|
|
|
|
|
|
@ -159,6 +175,67 @@ data Bar = Bar {
@@ -159,6 +175,67 @@ data Bar = Bar {
|
|
|
|
|
barVolume :: !Integer |
|
|
|
|
} deriving (Show, Eq, Generic) |
|
|
|
|
|
|
|
|
|
-- | Stores timeframe in seconds |
|
|
|
|
newtype BarTimeframe = BarTimeframe { unBarTimeframe :: Int } |
|
|
|
|
deriving (Show, Eq) |
|
|
|
|
|
|
|
|
|
serializeBar :: BarTimeframe -> Bar -> [ByteString] |
|
|
|
|
serializeBar tf bar = serializeBarHeader tf bar : [serializeBarBody tf bar] |
|
|
|
|
|
|
|
|
|
-- | Encodes bar header as tickerid:timeframe_seconds; |
|
|
|
|
-- Why ';' at the end? To support correct 0mq subscriptions. When we subscribe to topic, |
|
|
|
|
-- we actually subscribe by all topics which has requested subscription as a prefix. |
|
|
|
|
serializeBarHeader :: BarTimeframe -> Bar -> ByteString |
|
|
|
|
serializeBarHeader tf bar = |
|
|
|
|
B.fromStrict . E.encodeUtf8 $ (barSecurity bar) `T.append` encodeTimeframe tf |
|
|
|
|
where |
|
|
|
|
encodeTimeframe tf = mconcat [ ":", (T.pack . show $ unBarTimeframe tf), ";" ] |
|
|
|
|
|
|
|
|
|
serializeBarBody :: BarTimeframe -> Bar -> ByteString |
|
|
|
|
serializeBarBody tf bar = runPut $ do |
|
|
|
|
putWord32le 2 |
|
|
|
|
putWord32le $ fromIntegral $ unBarTimeframe tf |
|
|
|
|
putWord64le $ fromIntegral . toSeconds' . barTimestamp $ bar |
|
|
|
|
putWord32le $ fromIntegral . fracSeconds . barTimestamp $ bar |
|
|
|
|
putPrice $ barOpen bar |
|
|
|
|
putPrice $ barHigh bar |
|
|
|
|
putPrice $ barLow bar |
|
|
|
|
putPrice $ barClose bar |
|
|
|
|
putWord32le $ fromIntegral $ barVolume bar |
|
|
|
|
where |
|
|
|
|
fractionalPart :: (RealFrac a) => a -> a |
|
|
|
|
fractionalPart x = x - fromIntegral (truncate x) |
|
|
|
|
toSeconds' = floor . utcTimeToPOSIXSeconds |
|
|
|
|
fracSeconds t = (truncate $ (* 1000000000000) $ utcTimeToPOSIXSeconds t) `mod` 1000000000000 `div` 1000000 |
|
|
|
|
|
|
|
|
|
parseBar :: Get (BarTimeframe, Bar) |
|
|
|
|
parseBar = do |
|
|
|
|
packetType <- fromEnum <$> getWord32le |
|
|
|
|
when (packetType /= 2) $ fail "Expected packettype == 2" |
|
|
|
|
tf <- fromIntegral <$> getWord32le |
|
|
|
|
tsec <- getWord64le |
|
|
|
|
tusec <- getWord32le |
|
|
|
|
open_ <- parsePrice |
|
|
|
|
high_ <- parsePrice |
|
|
|
|
low_ <- parsePrice |
|
|
|
|
close_ <- parsePrice |
|
|
|
|
volume_ <- fromIntegral <$> getWord32le |
|
|
|
|
return (BarTimeframe tf, Bar { barSecurity = "", |
|
|
|
|
barTimestamp = makeTimestamp tsec tusec, |
|
|
|
|
barOpen = open_, |
|
|
|
|
barHigh = high_, |
|
|
|
|
barLow = low_, |
|
|
|
|
barClose = close_, |
|
|
|
|
barVolume = volume_ }) |
|
|
|
|
|
|
|
|
|
deserializeBar :: [ByteString] -> Maybe (BarTimeframe, Bar) |
|
|
|
|
deserializeBar (header:rawData:_) = case runGetOrFail parseBar rawData of |
|
|
|
|
Left (_, _, _) -> Nothing |
|
|
|
|
Right (_, _, (tf, bar)) -> Just $ (tf, bar { barSecurity = T.takeWhile (/= ':') . E.decodeUtf8 . B.toStrict $ header }) |
|
|
|
|
|
|
|
|
|
deserializeBar _ = Nothing |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
data SignalId = SignalId { |
|
|
|
|
strategyId :: T.Text, |
|
|
|
|
signalName :: T.Text, |
|
|
|
|
|