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.
 

185 lines
7.2 KiB

{-# LANGUAGE CPP #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
module TXMLConnector
(
start
, TXMLConnector.stop
, TXMLConnectorHandle
, makeRequest
, makeBrokerBackend
) where
import ATrade.Broker.Backend (BrokerBackend (..),
BrokerBackendNotification (..))
import ATrade.Logging (Message, Severity (..),
logWith)
import ATrade.QuoteSource.Server (QuoteSourceServerData (..))
import ATrade.Types (Order, OrderId)
import Colog (HasLog (getLogAction, setLogAction),
LogAction (LogAction, unLogAction))
import Config (TransaqConnectorConfig (..))
import Control.Concurrent (ThreadId)
import Control.Concurrent.BoundedChan (BoundedChan)
import Control.Concurrent.STM (TVar, atomically,
newEmptyTMVar, newEmptyTMVarIO,
newTVarIO, putTMVar, takeTMVar,
writeTVar)
import Control.Concurrent.STM.TBQueue (TBQueue, newTBQueue)
import Control.Concurrent.STM.TMVar (TMVar)
import Control.Monad (void)
import Control.Monad.IO.Class (MonadIO (liftIO))
import Control.Monad.Reader (ReaderT (runReaderT))
import Control.Monad.Reader.Class (MonadReader)
import qualified Data.Bimap as BM
import qualified Data.Map.Strict as M
import qualified Data.Text as T
import Data.Time.Clock (UTCTime, getCurrentTime)
import GHC.Exts (IsList (..))
import Prelude hiding (log)
import SlaveThread (fork)
import TickerInfoServer (TickerInfoServerHandle)
import TickTable (TickTable)
import Transaq (TransaqResponse)
import TXML (LogLevel, MonadTXML,
initialize, sendCommand,
setCallback)
import qualified TXML
import TXMLConnector.Internal (BrokerState (..),
ConnectionStage (..), Env (..),
Request (..), Response (..),
workThread)
#if defined(mingw32_HOST_OS)
import qualified Win32.TXML as TXMLImpl
#else
import qualified Linux.TXML as TXMLImpl
#endif
data ConnectionParams =
ConnectionParams
{ cpLogin :: T.Text
, cpPassword :: T.Text
, cpHost :: T.Text
, cpPort :: Int
, cpLogPath :: T.Text
, cpLogLevel :: LogLevel
}
deriving (Show, Eq, Ord)
data TXMLConnectorHandle =
TXMLConnectorHandle
{ threadId :: ThreadId
, notificationQueue :: TBQueue TransaqResponse
, hRequestVar :: TMVar Request
, hResponseVar :: TMVar (TMVar Response)
, hRequestTimestamp :: TVar UTCTime
, hNotificationCallback :: TVar (Maybe (BrokerBackendNotification -> IO ()))
, hRunVar :: TMVar ()
}
newtype App a = App { unApp :: ReaderT Env IO a }
deriving (Monad, Applicative, Functor, MonadIO, MonadReader Env)
instance MonadTXML App where
initialize path loglevel = liftIO $ TXMLImpl.initialize path loglevel
uninitialize = liftIO TXMLImpl.uninitialize
sendCommand = liftIO . TXMLImpl.sendCommand
setCallback = liftIO . TXMLImpl.setCallback
instance HasLog Env Message App where
getLogAction env = LogAction { unLogAction = liftIO . (unLogAction . logger $ env) }
setLogAction _ env = env -- fuck it
start ::
LogAction IO Message
-> TickTable
-> TransaqConnectorConfig
-> BoundedChan QuoteSourceServerData
-> TickerInfoServerHandle
-> IO TXMLConnectorHandle
start logger' tickTable config' qssChannel' tisH = do
logWith logger' Info "TXMLConnector" "Starting"
notificationQueue' <- atomically $ newTBQueue 50000
requestVar' <- newEmptyTMVarIO
responseVar' <- newEmptyTMVarIO
currentCandles' <- newTVarIO []
serverConnected' <- liftIO $ newTVarIO StageConnection
candleKindMap' <- newTVarIO M.empty
requestTimestamp' <- getCurrentTime >>= newTVarIO
orderMap <- newTVarIO M.empty
notificationCallback <- newTVarIO Nothing
orderTransactionIdMap <- newTVarIO BM.empty
pendingOrders <- newTVarIO (fromList [])
runVar' <- newEmptyTMVarIO
timerVar' <- newEmptyTMVarIO
let brokerState' =
BrokerState
{ bsOrderTransactionIdMap = orderTransactionIdMap
, bsNotificationCallback = notificationCallback
, bsOrderMap = orderMap
, bsPendingOrders = pendingOrders
}
let env =
Env
{ qssChannel = qssChannel'
, tisHandle = tisH
, requestVar = requestVar'
, responseVar = responseVar'
, requestTimestamp = requestTimestamp'
, currentCandles = currentCandles'
, tickMap = tickTable
, transaqQueue = notificationQueue'
, logger = logger'
, config = config'
, serverConnected = serverConnected'
, candleKindMap = candleKindMap'
, brokerState = brokerState'
, runVar = runVar'
, timerVar = timerVar'
}
workThreadId <- fork $ (runReaderT . unApp) workThread env
return $ TXMLConnectorHandle
{ threadId = workThreadId
, notificationQueue = notificationQueue'
, hRequestVar = requestVar'
, hResponseVar = responseVar'
, hRequestTimestamp = requestTimestamp'
, hNotificationCallback = notificationCallback
, hRunVar = runVar'
}
stop :: TXMLConnectorHandle -> IO ()
stop h = atomically $ putTMVar (hRunVar h) ()
brSubmitOrder :: TXMLConnectorHandle -> Order -> IO ()
brSubmitOrder h order = void $ makeRequest h (RequestSubmitOrder order)
brCancelOrder :: TXMLConnectorHandle -> OrderId -> IO ()
brCancelOrder h oid = void $ makeRequest h (RequestCancelOrder oid)
brSetNotificationCallback :: TXMLConnectorHandle -> Maybe (BrokerBackendNotification -> IO ()) -> IO ()
brSetNotificationCallback h cb = atomically $ writeTVar (hNotificationCallback h) cb
makeBrokerBackend :: TXMLConnectorHandle -> T.Text -> BrokerBackend
makeBrokerBackend h accountId =
BrokerBackend [accountId] (brSetNotificationCallback h) (brSubmitOrder h) (brCancelOrder h) (TXMLConnector.stop h)
makeRequest :: TXMLConnectorHandle -> Request -> IO Response
makeRequest h request = do
now <- getCurrentTime
resp <- atomically $ do
resp <- newEmptyTMVar
writeTVar (hRequestTimestamp h) now
putTMVar (hResponseVar h) resp
putTMVar (hRequestVar h) request
pure resp
atomically $ do
void $ takeTMVar (hResponseVar h)
takeTMVar resp