|
|
|
@ -6,27 +6,40 @@ module ATrade.MDS.HistoryServer ( |
|
|
|
startHistoryServer |
|
|
|
startHistoryServer |
|
|
|
) where |
|
|
|
) where |
|
|
|
|
|
|
|
|
|
|
|
import ATrade.MDS.Database |
|
|
|
import ATrade.MDS.Database (MdsHandle, |
|
|
|
import ATrade.MDS.Protocol |
|
|
|
TimeInterval (TimeInterval), |
|
|
|
import ATrade.Types |
|
|
|
Timeframe (Timeframe), |
|
|
|
import Control.Concurrent |
|
|
|
getDataConduit, putData) |
|
|
|
import Control.Monad |
|
|
|
import ATrade.MDS.Protocol (HAPRequest (hapEndTime, hapStartTime, hapTicker, hapTimeframeSec), |
|
|
|
import Control.Monad.IO.Class |
|
|
|
QHPRequest (rqCompression, rqEndTime, rqPeriod, rqStartTime, rqTicker), |
|
|
|
import Data.Aeson |
|
|
|
periodSeconds) |
|
|
|
import Data.Binary.Get |
|
|
|
import ATrade.Types (Bar (..), fromDouble, toDouble) |
|
|
|
import Data.Binary.Put |
|
|
|
import Control.Concurrent (ThreadId, forkIO) |
|
|
|
|
|
|
|
import Control.Monad (forever) |
|
|
|
|
|
|
|
import Control.Monad.IO.Class (MonadIO (..)) |
|
|
|
|
|
|
|
import Data.Aeson (decode) |
|
|
|
|
|
|
|
import Data.Binary.Get (getDoublele, getWord64le, |
|
|
|
|
|
|
|
runGetOrFail) |
|
|
|
|
|
|
|
import Data.Binary.Put (putDoublele, putWord64le, runPut) |
|
|
|
import qualified Data.ByteString as B |
|
|
|
import qualified Data.ByteString as B |
|
|
|
import qualified Data.ByteString.Lazy as BL |
|
|
|
import qualified Data.ByteString.Lazy as BL |
|
|
|
import Data.Conduit as C |
|
|
|
import Data.Conduit as C (ConduitT, Void, awaitForever, |
|
|
|
import Data.Conduit.Combinators |
|
|
|
runConduit, yield, (.|)) |
|
|
|
import Data.List.NonEmpty |
|
|
|
import Data.Conduit.Combinators (conduitVector) |
|
|
|
|
|
|
|
import qualified Data.Conduit.Combinators as CC |
|
|
|
|
|
|
|
import qualified Data.Conduit.Zlib as CZ |
|
|
|
|
|
|
|
import Data.List.NonEmpty (NonEmpty ((:|))) |
|
|
|
import qualified Data.Text as T |
|
|
|
import qualified Data.Text as T |
|
|
|
import Data.Time.Clock.POSIX |
|
|
|
import Data.Time.Clock.POSIX (posixSecondsToUTCTime, |
|
|
|
|
|
|
|
utcTimeToPOSIXSeconds) |
|
|
|
import qualified Data.Vector as V |
|
|
|
import qualified Data.Vector as V |
|
|
|
import Safe |
|
|
|
import Safe (atMay, headMay) |
|
|
|
|
|
|
|
|
|
|
|
import System.Log.Logger |
|
|
|
import System.Log.Logger (debugM) |
|
|
|
import System.ZMQ4 |
|
|
|
import System.ZMQ4 (Context, Flag (SendMore), Receiver, |
|
|
|
|
|
|
|
Router (Router), Sender, Socket, |
|
|
|
|
|
|
|
bind, receiveMulti, send, sendMulti, |
|
|
|
|
|
|
|
socket) |
|
|
|
|
|
|
|
|
|
|
|
data HistoryServer = HistoryServer ThreadId ThreadId |
|
|
|
data HistoryServer = HistoryServer ThreadId ThreadId |
|
|
|
|
|
|
|
|
|
|
|
@ -59,17 +72,23 @@ serveQHP db sock = forever $ do |
|
|
|
handleCmd peerId cmd = case cmd of |
|
|
|
handleCmd peerId cmd = case cmd of |
|
|
|
rq -> do |
|
|
|
rq -> do |
|
|
|
debugM "QHP" $ "Incoming command: " ++ show cmd |
|
|
|
debugM "QHP" $ "Incoming command: " ++ show cmd |
|
|
|
|
|
|
|
let compressionConduit = if rqCompression rq == Just "gzip" then CZ.gzip else CC.map id |
|
|
|
let dataC = getDataConduit db (replaceWildcards $ rqTicker rq) (TimeInterval (rqStartTime rq) (rqEndTime rq)) (Timeframe (periodSeconds $ rqPeriod rq)) |
|
|
|
let dataC = getDataConduit db (replaceWildcards $ rqTicker rq) (TimeInterval (rqStartTime rq) (rqEndTime rq)) (Timeframe (periodSeconds $ rqPeriod rq)) |
|
|
|
runConduit $ dataC .| (conduitVector chunkSize) .| (sendChunks peerId) |
|
|
|
runConduit $ dataC .| conduitVector chunkSize .| serializeChunk .| compressionConduit .| sendBSChunks peerId |
|
|
|
sendChunks :: (MonadIO m) => B.ByteString -> ConduitT (V.Vector Bar) Void m () |
|
|
|
|
|
|
|
sendChunks peerId = do |
|
|
|
serializeChunk :: (MonadIO m) => ConduitT (V.Vector Bar) B.ByteString m () |
|
|
|
|
|
|
|
serializeChunk = awaitForever $ \vBars -> yield $ BL.toStrict $ serializeBars vBars |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
sendBSChunks :: (MonadIO m) => B.ByteString -> ConduitT B.ByteString Void m () |
|
|
|
|
|
|
|
sendBSChunks peerId = do |
|
|
|
liftIO $ send sock [SendMore] peerId |
|
|
|
liftIO $ send sock [SendMore] peerId |
|
|
|
liftIO $ send sock [SendMore] B.empty |
|
|
|
liftIO $ send sock [SendMore] B.empty |
|
|
|
liftIO $ send sock [SendMore] "OK" |
|
|
|
liftIO $ send sock [SendMore] "OK" |
|
|
|
awaitForever $ \vBars -> liftIO $ do |
|
|
|
awaitForever $ \bs -> liftIO $ do |
|
|
|
debugM "QHP" $ "Sending chunk: " ++ show (V.length vBars) ++ " bars" |
|
|
|
debugM "QHP" $ "Sending chunk: " ++ show (B.length bs) ++ " bytes" |
|
|
|
send sock [SendMore] $ BL.toStrict $ serializeBars vBars |
|
|
|
send sock [SendMore] bs |
|
|
|
liftIO $ send sock [] B.empty |
|
|
|
liftIO $ send sock [] B.empty |
|
|
|
|
|
|
|
|
|
|
|
serializeBars :: V.Vector Bar -> BL.ByteString |
|
|
|
serializeBars :: V.Vector Bar -> BL.ByteString |
|
|
|
serializeBars bars = runPut $ V.forM_ bars serializeBar' |
|
|
|
serializeBars bars = runPut $ V.forM_ bars serializeBar' |
|
|
|
serializeBar' bar = do |
|
|
|
serializeBar' bar = do |
|
|
|
@ -80,7 +99,7 @@ serveQHP db sock = forever $ do |
|
|
|
putDoublele (toDouble . barClose $ bar) |
|
|
|
putDoublele (toDouble . barClose $ bar) |
|
|
|
putWord64le (fromInteger . barVolume $ bar) |
|
|
|
putWord64le (fromInteger . barVolume $ bar) |
|
|
|
|
|
|
|
|
|
|
|
chunkSize = 4096 |
|
|
|
chunkSize = 16384 |
|
|
|
|
|
|
|
|
|
|
|
replaceWildcards = T.map mapWildcard |
|
|
|
replaceWildcards = T.map mapWildcard |
|
|
|
mapWildcard '?' = '_' |
|
|
|
mapWildcard '?' = '_' |
|
|
|
|