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.

258 lines
12 KiB

{-# LANGUAGE CPP #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
3 years ago
module TXMLConnector
(
3 years ago
start
, TXMLConnector.stop
3 years ago
, TXMLConnectorHandle
, makeRequest
, makeBrokerBackend
3 years ago
) where
import ATrade.Logging (Message, Severity (..), log,
3 years ago
logWith)
import Colog (HasLog (getLogAction, setLogAction),
LogAction (LogAction, unLogAction))
3 years ago
import Config (SubscriptionConfig (SubscriptionConfig),
TransaqConnectorConfig (..),
transaqHost, transaqLogLevel,
transaqLogPath, transaqLogin,
transaqPassword, transaqPort)
import Control.Concurrent (ThreadId, forkIO, threadDelay)
import Control.Concurrent.STM (TVar, atomically, modifyTVar',
3 years ago
newEmptyTMVar, newEmptyTMVarIO,
newTVarIO, orElse, putTMVar,
readTMVar, readTVar,
readTVarIO, takeTMVar,
tryPutTMVar, tryReadTMVar,
writeTVar)
3 years ago
import Control.Concurrent.STM.TBQueue (TBQueue, flushTBQueue,
newTBQueue, readTBQueue,
writeTBQueue)
import Control.Monad (forM_, forever, void, when)
import Control.Monad.Extra (whileM)
import qualified Data.Bimap as BM
3 years ago
import Data.Maybe (mapMaybe)
import qualified Data.Text as T
import qualified Deque.Strict as D
3 years ago
import Text.XML.Light.Input (parseXML)
import Text.XML.Light.Types (Content (Elem),
Element (elName),
QName (qName))
3 years ago
import TickTable (TickTable, insertTick,
lookupTick, newTickTable)
3 years ago
import Transaq (AllTradesTrade (..),
Candle (..), ClientData (..),
3 years ago
CommandChangePass (..),
3 years ago
CommandConnect (..),
CommandDisconnect (CommandDisconnect),
CommandGetHistoryData (CommandGetHistoryData),
CommandServerStatus (..),
3 years ago
CommandSubscribe (..),
ConnectionState (Disconnected),
Language (LanguageEn),
MarketInfo (..),
OrderNotification (..),
OrderStatus (..),
3 years ago
Quotation (..),
ResponseAllTrades (ResponseAllTrades),
ResponseCandleKinds (ResponseCandleKinds),
3 years ago
ResponseCandles (..),
ResponseCandlesStatus (StatusPending),
ResponseClient (ResponseClient),
3 years ago
ResponseMarkets (ResponseMarkets),
ResponseOrders (ResponseOrders),
3 years ago
ResponseQuotations (ResponseQuotations),
ResponseQuotes (ResponseQuotes),
ResponseResult (..),
3 years ago
ResponseSecurities (ResponseSecurities),
ResponseTrades (ResponseTrades),
3 years ago
Security (..), SecurityId (..),
TradeNotification (..),
3 years ago
TransaqCommand (toXml),
TransaqResponse (..),
TransaqResponse (..),
TransaqResponseC (fromXml),
UnfilledAction (..),
kCandleKindId, kPeriod, state)
import TXML (LogLevel, MonadTXML,
freeCallback, initialize,
sendCommand, setCallback)
3 years ago
import ATrade.Broker.Backend (BrokerBackend (..),
BrokerBackendNotification (..))
3 years ago
import ATrade.QuoteSource.Server (QuoteSourceServerData (..))
3 years ago
import ATrade.Types (Bar (..),
BarTimeframe (unBarTimeframe),
DataType (BestBid, BestOffer, LastTradePrice),
Order (..), OrderId,
OrderPrice (..),
OrderState (..), Tick (..),
TickerId, Trade (..),
fromDouble, toDouble)
import qualified ATrade.Types as AT
import Colog.Monad (WithLog)
3 years ago
import Control.Applicative ((<|>))
3 years ago
import Control.Concurrent.BoundedChan (BoundedChan, writeChan)
3 years ago
import Control.Concurrent.STM.TMVar (TMVar)
3 years ago
import Control.Error (headMay)
import Control.Monad.IO.Class (MonadIO (liftIO))
import Control.Monad.Reader (ReaderT (runReaderT))
import Control.Monad.Reader.Class (MonadReader, asks)
import Data.Int (Int64)
3 years ago
import qualified Data.Map.Strict as M
import Data.Time.Clock (UTCTime, diffUTCTime,
getCurrentTime)
import FSM (FSMCallback (..),
FSMState (isTerminalState),
makeFsm, runFsm)
import GHC.Exts (IsList (..))
import Prelude hiding (log)
3 years ago
import TickerInfoServer (TickerInfo (..),
TickerInfoServerHandle,
putTickerInfo)
3 years ago
import qualified Transaq
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
3 years ago
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 ()
3 years ago
}
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
3 years ago
start ::
LogAction IO Message
-> TransaqConnectorConfig
-> BoundedChan QuoteSourceServerData
3 years ago
-> TickerInfoServerHandle
3 years ago
-> IO TXMLConnectorHandle
3 years ago
start logger config qssChannel tisH = do
3 years ago
logWith logger Info "TXMLConnector" "Starting"
notificationQueue <- atomically $ newTBQueue 50000
3 years ago
tickTable <- newTickTable
requestVar <- newEmptyTMVarIO
responseVar <- newEmptyTMVarIO
3 years ago
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
3 years ago
, currentCandles = currentCandles
, tickMap = tickTable
, transaqQueue = notificationQueue
, logger = logger
, config = config
, serverConnected = serverConnected
, candleKindMap = candleKindMap
, brokerState = brokerState
, runVar = runVar
, timerVar = timerVar
}
threadId <- forkIO $ (runReaderT . unApp) workThread env
return $ TXMLConnectorHandle
{
threadId = threadId
, notificationQueue = notificationQueue
, hRequestVar = requestVar
, hResponseVar = responseVar
, hRequestTimestamp = requestTimestamp
, hNotificationCallback = notificationCallback
, hRunVar = runVar
}
3 years ago
stop :: TXMLConnectorHandle -> IO ()
stop h = atomically $ putTMVar (hRunVar h) ()
brSubmitOrder :: TXMLConnectorHandle -> Order -> IO ()
brSubmitOrder h order = void $ makeRequest h (RequestSubmitOrder order)
3 years ago
brCancelOrder :: TXMLConnectorHandle -> OrderId -> IO ()
brCancelOrder h oid = void $ makeRequest h (RequestCancelOrder oid)
3 years ago
brSetNotificationCallback :: TXMLConnectorHandle -> Maybe (BrokerBackendNotification -> IO ()) -> IO ()
brSetNotificationCallback h cb = atomically $ writeTVar (hNotificationCallback h) cb
makeBrokerBackend :: TXMLConnectorHandle -> T.Text -> BrokerBackend
makeBrokerBackend h account =
BrokerBackend [account] (brSetNotificationCallback h) (brSubmitOrder h) (brCancelOrder h) (TXMLConnector.stop h)
3 years ago
makeRequest :: TXMLConnectorHandle -> Request -> IO Response
makeRequest h request = do
now <- getCurrentTime
3 years ago
resp <- atomically $ do
resp <- newEmptyTMVar
writeTVar (hRequestTimestamp h) now
3 years ago
putTMVar (hResponseVar h) resp
putTMVar (hRequestVar h) request
pure resp
atomically $ do
void $ takeTMVar (hResponseVar h)
takeTMVar resp