Browse Source

BrokerCLient: submit order

master
Denis Tereshkin 9 years ago
parent
commit
0f7e2f49e7
  1. 2
      libatrade.cabal
  2. 57
      src/ATrade/Broker/Client.hs
  3. 7
      test/Spec.hs
  4. 15
      test/TestBrokerClient.hs
  5. 2
      test/TestQuoteSourceClient.hs

2
libatrade.cabal

@ -19,6 +19,7 @@ library
exposed-modules: ATrade.Types exposed-modules: ATrade.Types
, ATrade.QuoteSource.Client , ATrade.QuoteSource.Client
, ATrade.QuoteSource.Server , ATrade.QuoteSource.Server
, ATrade.Broker.Client
, ATrade.Broker.Protocol , ATrade.Broker.Protocol
, ATrade.Broker.Server , ATrade.Broker.Server
, ATrade.Util , ATrade.Util
@ -76,6 +77,7 @@ test-suite libatrade-test
default-language: Haskell2010 default-language: Haskell2010
other-modules: ArbitraryInstances other-modules: ArbitraryInstances
, MockBroker , MockBroker
, TestBrokerClient
, TestBrokerProtocol , TestBrokerProtocol
, TestBrokerServer , TestBrokerServer
, TestQuoteSourceClient , TestQuoteSourceClient

57
src/ATrade/Broker/Client.hs

@ -1,6 +1,10 @@
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
module ATrade.Broker.Client ( module ATrade.Broker.Client (
startBrokerClient,
stopBrokerClient,
submitOrder,
cancelOrder
) where ) where
import ATrade.Types import ATrade.Types
@ -9,6 +13,10 @@ import Control.Concurrent hiding (readChan, writeChan)
import Control.Concurrent.BoundedChan import Control.Concurrent.BoundedChan
import Control.Concurrent.MVar import Control.Concurrent.MVar
import Control.Exception import Control.Exception
import Control.Monad
import Data.Aeson
import Data.Int
import Data.IORef
import Data.List.NonEmpty import Data.List.NonEmpty
import qualified Data.Text as T import qualified Data.Text as T
import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString.Lazy as BL
@ -18,15 +26,58 @@ import System.Log.Logger
data BrokerClientHandle = BrokerClientHandle { data BrokerClientHandle = BrokerClientHandle {
tid :: ThreadId, tid :: ThreadId,
completionMvar :: compMv, completionMvar :: MVar (),
submitOrder :: Order -> IO (Either T.Text OrderId), submitOrder :: Order -> IO (Either T.Text OrderId),
cancelOrder :: OrderId -> IO (Either T.Text ()), cancelOrder :: OrderId -> IO (Either T.Text ()),
cmdVar :: MVar BrokerServerRequest, cmdVar :: MVar BrokerServerRequest,
respVar :: MVar BrokerServerResponse respVar :: MVar BrokerServerResponse
} }
brokerClientThread ctx ep cmd resp comp = do
sock <- socket ctx Req
connect sock $ T.unpack ep
finally (brokerClientThread' sock) (cleanup sock)
where
cleanup sock = close sock >> putMVar comp ()
brokerClientThread' sock = forever $ do
request <- readMVar cmd
send sock [] (BL.toStrict $ encode request)
maybeResponse <- decode . BL.fromStrict <$> receive sock
case maybeResponse of
Just response -> putMVar resp response
Nothing -> putMVar resp (ResponseError "Unable to decode response")
startBrokerClient :: Context -> T.Text -> IO BrokerClientHandle startBrokerClient :: Context -> T.Text -> IO BrokerClientHandle
startBrokerClient ctx endpoint = undefined startBrokerClient ctx endpoint = do
idCounter <- newIORef 1
compMv <- newEmptyMVar
cmdVar <- newEmptyMVar :: IO (MVar BrokerServerRequest)
respVar <- newEmptyMVar :: IO (MVar BrokerServerResponse)
tid <- forkIO (brokerClientThread ctx endpoint cmdVar respVar compMv)
return BrokerClientHandle {
tid = tid,
completionMvar = compMv,
submitOrder = bcSubmitOrder idCounter cmdVar respVar,
cancelOrder = bcCancelOrder idCounter cmdVar respVar,
cmdVar = cmdVar,
respVar = respVar
}
stopBrokerClient :: BrokerClientHandle -> IO () stopBrokerClient :: BrokerClientHandle -> IO ()
stopBrokerClient handle = undefined stopBrokerClient handle = yield >> killThread (tid handle) >> readMVar (completionMvar handle)
nextId cnt = atomicModifyIORef' cnt (\v -> (v + 1, v))
bcSubmitOrder :: IORef Int64 -> MVar BrokerServerRequest -> MVar BrokerServerResponse -> Order -> IO (Either T.Text OrderId)
bcSubmitOrder idCounter cmdVar respVar order = do
sqnum <- nextId idCounter
putMVar cmdVar (RequestSubmitOrder sqnum order)
resp <- readMVar respVar
case resp of
(ResponseOrderSubmitted oid) -> return $ Right oid
(ResponseError msg) -> return $ Left msg
bcCancelOrder idCounter cmdVar respVar orderId = undefined

7
test/Spec.hs

@ -1,5 +1,6 @@
import qualified TestTypes import qualified TestTypes
import qualified TestBrokerClient
import qualified TestBrokerProtocol import qualified TestBrokerProtocol
import qualified TestBrokerServer import qualified TestBrokerServer
import qualified TestQuoteSourceClient import qualified TestQuoteSourceClient
@ -14,7 +15,9 @@ properties :: TestTree
properties = testGroup "Properties" [TestTypes.properties, TestBrokerProtocol.properties] properties = testGroup "Properties" [TestTypes.properties, TestBrokerProtocol.properties]
unitTests :: TestTree unitTests :: TestTree
unitTests = testGroup "Unit-tests" [TestQuoteSourceClient.unitTests unitTests = testGroup "Unit-tests" [
TestQuoteSourceClient.unitTests
, TestQuoteSourceServer.unitTests , TestQuoteSourceServer.unitTests
, TestBrokerServer.unitTests] , TestBrokerServer.unitTests
, TestBrokerClient.unitTests]

15
test/TestBrokerClient.hs

@ -1,7 +1,7 @@
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
module TestBrokerServer ( module TestBrokerClient (
unitTests unitTests
) where ) where
@ -13,7 +13,8 @@ import Test.Tasty.HUnit
import ATrade.Types import ATrade.Types
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 ATrade.Broker.Server import ATrade.Broker.Client
import ATrade.Broker.Server hiding (submitOrder)
import ATrade.Broker.Protocol import ATrade.Broker.Protocol
import ATrade.Util import ATrade.Util
import qualified Data.Text as T import qualified Data.Text as T
@ -34,7 +35,7 @@ import Data.UUID as U
import Data.UUID.V4 as UV4 import Data.UUID.V4 as UV4
import MockBroker import MockBroker
unitTests = testGroup "Broker.Client" [] unitTests = testGroup "Broker.Client" [testBrokerClientStartStop]
makeEndpoint = do makeEndpoint = do
uid <- toText <$> UV4.nextRandom uid <- toText <$> UV4.nextRandom
@ -48,11 +49,13 @@ defaultOrder = mkOrder {
orderOperation = Buy orderOperation = Buy
} }
testBrokerClientStartStop = testCase "Broker client starts and stops" $ withContext (\ctx -> do testBrokerClientStartStop = testCase "Broker client: submit order" $ withContext (\ctx -> do
ep <- makeEndpoint ep <- makeEndpoint
(mockBroker, broState) <- mkMockBroker ["demo"] (mockBroker, broState) <- mkMockBroker ["demo"]
bracket (startBrokerServer [mockBroker] ctx ep) stopBrokerServer (\broS -> bracket (startBrokerServer [mockBroker] ctx ep) stopBrokerServer (\broS ->
bracket (startBrokerClient ctx ep) stopBrokerClient (\broC -> bracket (startBrokerClient ctx ep) stopBrokerClient (\broC -> do
oid <- submitOrder broC defaultOrder oid <- submitOrder broC defaultOrder
))) case oid of
Left err -> assertFailure "Invalid response"
Right _ -> return ())))

2
test/TestQuoteSourceClient.hs

@ -52,7 +52,9 @@ testTickStream = testCase "QuoteSource clients receives ticks" $ withContext (\c
timestamp = UTCTime (fromGregorian 2016 9 27) 16000, timestamp = UTCTime (fromGregorian 2016 9 27) 16000,
value = 1000, value = 1000,
volume = 1} volume = 1}
yield
writeChan chan (Just tick) writeChan chan (Just tick)
yield
recvdTick <- readChan clientChan recvdTick <- readChan clientChan
tick @=? recvdTick))) tick @=? recvdTick)))

Loading…
Cancel
Save