Browse Source

Update for changed libatrade API

master
Denis Tereshkin 9 years ago
parent
commit
9ac9e8a713
  1. 30
      app/Main.hs
  2. 10
      quik-connector.cabal
  3. 22
      src/Broker/PaperBroker.hs
  4. 44
      src/Broker/Protocol.hs
  5. 33
      src/Broker/QuikBroker.hs
  6. 5
      src/Broker/QuikBroker/HsQuik.hs
  7. 9
      src/Network/Telegram.hs
  8. 2
      stack.yaml

30
app/Main.hs

@ -1,4 +1,4 @@
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings, OverloadedLabels #-}
module Main where module Main where
import System.IO import System.IO
@ -10,13 +10,16 @@ import Control.Exception
import Control.Error.Util import Control.Error.Util
import Control.Monad.IO.Class import Control.Monad.IO.Class
import Data.IORef import Data.IORef
import Graphics.UI.Gtk hiding (Action, backspace) import qualified GI.Gtk as Gtk
import Data.GI.Base
import Control.Concurrent.BoundedChan import Control.Concurrent.BoundedChan
import ATrade.Types import ATrade.Types
import QuoteSource.TableParsers.AllParamsTableParser import QuoteSource.TableParsers.AllParamsTableParser
import QuoteSource.TableParser import QuoteSource.TableParser
import ATrade.QuoteSource.Server import ATrade.QuoteSource.Server
import ATrade.Broker.TradeSinks.ZMQTradeSink
import ATrade.Broker.TradeSinks.TelegramTradeSink
import ATrade.Broker.Server import ATrade.Broker.Server
import ATrade.Broker.Protocol import ATrade.Broker.Protocol
import Broker.PaperBroker import Broker.PaperBroker
@ -158,9 +161,7 @@ main = do
broker <- mkPaperBroker c1 1000000 ["demo"] broker <- mkPaperBroker c1 1000000 ["demo"]
man <- newManager (mkManagerSettings (TLSSettingsSimple { settingDisableCertificateValidation = True, settingDisableSession = False, settingUseServerName = False }) Nothing) man <- newManager (mkManagerSettings (TLSSettingsSimple { settingDisableCertificateValidation = True, settingDisableSession = False, settingUseServerName = False }) Nothing)
infoM "main" "Http manager created" infoM "main" "Http manager created"
eitherBrokerQ <- runExceptT $ mkQuikBroker man (dllPath config) (quikPath config) (quikAccounts config) (Just (telegramToken config, telegramChatId config)) eitherBrokerQ <- runExceptT $ mkQuikBroker man (dllPath config) (quikPath config) (quikAccounts config)
tgCtx <- mkTelegramContext man (telegramToken config)
sendMessage tgCtx (telegramChatId config) "Goldmine-Quik connector started"
case eitherBrokerQ of case eitherBrokerQ of
Left errmsg -> warningM "main" $ "Can't load quik broker: " ++ T.unpack errmsg Left errmsg -> warningM "main" $ "Can't load quik broker: " ++ T.unpack errmsg
Right brokerQ -> Right brokerQ ->
@ -187,17 +188,16 @@ main = do
let serverParams = defaultServerSecurityParams { sspDomain = Just "global", let serverParams = defaultServerSecurityParams { sspDomain = Just "global",
sspCertificate = serverCert } sspCertificate = serverCert }
withZMQTradeSink ctx (tradeSink config) (\zmqTradeSink ->
withTelegramTradeSink (telegramToken config) (telegramChatId config) (\telegramTradeSink ->
bracket (startQuoteSourceServer c2 ctx (T.pack $ quotesourceEndpoint config)) stopQuoteSourceServer (\qsServer -> do bracket (startQuoteSourceServer c2 ctx (T.pack $ quotesourceEndpoint config)) stopQuoteSourceServer (\qsServer -> do
bracket (startBrokerServer [broker, brokerQ] ctx (T.pack $ brokerserverEndpoint config) (tradeSink config) serverParams) stopBrokerServer (\broServer -> do bracket (startBrokerServer [broker, brokerQ] ctx (T.pack $ brokerserverEndpoint config) [telegramTradeSink, zmqTradeSink] serverParams) stopBrokerServer (\broServer -> do
void initGUI Gtk.init Nothing
window <- windowNew window <- new Gtk.Window [ #title := "Quik connector" ]
window `on` deleteEvent $ do on window #destroy Gtk.mainQuit
liftIO mainQuit #showAll window
return False Gtk.main)
widgetShowAll window infoM "main" "BRS down")))))
mainGUI)
infoM "main" "BRS down")
))
killThread forkId killThread forkId
infoM "main" "Main thread done" infoM "main" "Main thread done"

10
quik-connector.cabal

@ -26,7 +26,8 @@ library
ghc-options: -Wincomplete-patterns ghc-options: -Wincomplete-patterns
build-depends: base >= 4.7 && < 5 build-depends: base >= 4.7 && < 5
, Win32 , Win32
, gtk , haskell-gi-base
, gi-gtk
, binary , binary
, data-binary-ieee754 , data-binary-ieee754
, bytestring , bytestring
@ -66,7 +67,7 @@ library
, incremental-parser , incremental-parser
, attoparsec , attoparsec
default-language: Haskell2010 default-language: Haskell2010
extra-libraries: "user32" -- extra-libraries: "user32"
other-modules: System.Win32.XlParser other-modules: System.Win32.XlParser
, System.Win32.DDE , System.Win32.DDE
@ -78,7 +79,8 @@ executable quik-connector-exe
build-depends: base build-depends: base
, quik-connector , quik-connector
, Win32 , Win32
, gtk , haskell-gi-base
, gi-gtk
, BoundedChan , BoundedChan
, hslogger , hslogger
, aeson , aeson
@ -98,7 +100,7 @@ executable quik-connector-exe
, directory , directory
, errors , errors
default-language: Haskell2010 default-language: Haskell2010
extra-libraries: "user32" -- extra-libraries: "user32"
test-suite quik-connector-test test-suite quik-connector-test
type: exitcode-stdio-1.0 type: exitcode-stdio-1.0

22
src/Broker/PaperBroker.hs

@ -104,12 +104,12 @@ executePendingOrders tick state = do
_ -> return Nothing _ -> return Nothing
executeLimitAt price order = case orderOperation order of executeLimitAt price order = case orderOperation order of
Buy -> if (datatype tick == Price && price > value tick) || (datatype tick == BestOffer && price > value tick) Buy -> if (datatype tick == Price && price > value tick && value tick > 0) || (datatype tick == BestOffer && price > value tick && value tick > 0)
then do then do
executeAtTick state order $ tick { value = price } executeAtTick state order $ tick { value = price }
return $ Just $ orderId order return $ Just $ orderId order
else return Nothing else return Nothing
Sell -> if (datatype tick == Price && price < value tick) || (datatype tick == BestBid && price < value tick) Sell -> if (datatype tick == Price && price < value tick && value tick > 0) || (datatype tick == BestBid && price < value tick && value tick > 0)
then do then do
executeAtTick state order $ tick { value = price } executeAtTick state order $ tick { value = price }
return $ Just $ orderId order return $ Just $ orderId order
@ -146,6 +146,12 @@ executeAtTick state order tick = do
maybeCall notificationCallback state $ TradeNotification $ mkTrade tick order ts maybeCall notificationCallback state $ TradeNotification $ mkTrade tick order ts
maybeCall notificationCallback state $ OrderNotification (orderId order) Executed maybeCall notificationCallback state $ OrderNotification (orderId order) Executed
rejectOrder state order = do
let newOrder = order { orderState = Rejected } in
atomicModifyIORef' state (\s -> (s { orders = M.insert (orderId order) newOrder $ orders s }, ()))
maybeCall notificationCallback state $ OrderNotification (orderId order) Submitted
maybeCall notificationCallback state $ OrderNotification (orderId order) Rejected
pbSubmitOrder :: IORef PaperBrokerState -> Order -> IO () pbSubmitOrder :: IORef PaperBrokerState -> Order -> IO ()
pbSubmitOrder state order = do pbSubmitOrder state order = do
infoM "PaperBroker" $ "Submitted order: " ++ show order infoM "PaperBroker" $ "Submitted order: " ++ show order
@ -159,11 +165,13 @@ pbSubmitOrder state order = do
executeMarketOrder state order = do executeMarketOrder state order = do
tm <- tickMap <$> readIORef state tm <- tickMap <$> readIORef state
case M.lookup key tm of case M.lookup key tm of
Nothing -> let newOrder = order { orderState = OrderError } in Nothing -> rejectOrder state order
atomicModifyIORef' state (\s -> (s { orders = M.insert (orderId order) newOrder $ orders s }, ())) Just tick -> if orderQuantity order /= 0
then executeAtTick state order tick
Just tick -> executeAtTick state order tick else rejectOrder state order
submitLimitOrder price state order = do submitLimitOrder price state order = if orderQuantity order == 0
then rejectOrder state order
else do
tm <- tickMap <$> readIORef state tm <- tickMap <$> readIORef state
case M.lookup key tm of case M.lookup key tm of
Nothing -> do Nothing -> do

44
src/Broker/Protocol.hs

@ -1,44 +0,0 @@
{-# LANGUAGE OverloadedStrings #-}
module Broker.Protocol (
) where
import qualified Data.HashMap.Strict as HM
import qualified Data.Text as T
import Data.Aeson
import Data.Int
import Broker
type RequestSqnum = Int64
data BrokerServerRequest = RequestSubmitOrder RequestSqnum Order
| RequestCancelOrder RequestSqnum OrderId
| RequestNotifications RequestSqnum
data BrokerServerResponse = ResponseOrderSubmitted OrderId
| ResponseOrderCancelled
| ResponseNotifications [Notification]
data Notification = OrderNotification OrderId OrderState | TradeNotification Trade
instance FromJSON Notification where
parseJSON = withObject "notification" (\obj -> do
tradeJson <- obj .: "trade"
case tradeJson of
Just v -> parseTrade v
Nothing -> do
orderNotification <- obj .: "order-state"
case orderNotification of
Just v -> parseOrder v
Nothing -> fail "Invalid notification")
where
parseTrade v = TradeNotification <$> parseJSON v
parseOrder (Object o) = case HM.lookup "order-state" o of
Just v -> withObject "object" (\os -> do
oid <- os .: "order-id"
ns <- os .: "new-state"
return $ OrderNotification oid ns) v
Nothing -> fail "Should be order-state"
instance ToJSON Notification where
toJSON (OrderNotification oid

33
src/Broker/QuikBroker.hs

@ -40,9 +40,7 @@ data QuikBrokerState = QuikBrokerState {
orderMap :: M.Map OrderId Order, orderMap :: M.Map OrderId Order,
orderIdMap :: BM.Bimap QuikOrderId OrderId, orderIdMap :: BM.Bimap QuikOrderId OrderId,
trans2orderid :: M.Map Integer Order, trans2orderid :: M.Map Integer Order,
transIdCounter :: Integer, transIdCounter :: Integer
messageChan :: BoundedChan T.Text,
messageTid :: Maybe ThreadId
} }
nextTransId state = atomicModifyIORef' state (\s -> (s { transIdCounter = transIdCounter s + 1 }, transIdCounter s)) nextTransId state = atomicModifyIORef' state (\s -> (s { transIdCounter = transIdCounter s + 1 }, transIdCounter s))
@ -53,26 +51,11 @@ maybeCall proj state arg = do
Just callback -> callback arg Just callback -> callback arg
Nothing -> return () Nothing -> return ()
messageThread tgCtx chatId msgChan = forever $ do mkQuikBroker :: Manager -> FilePath -> FilePath -> [T.Text] -> ExceptT T.Text IO BrokerInterface
maybeMsg <- tryReadChan msgChan mkQuikBroker man dllPath quikPath accs = do
case maybeMsg of
Just msg -> do
sendMessage tgCtx chatId msg
warningM "Quik.Telegram" $ "Telegram message sent: " ++ T.unpack msg
Nothing -> threadDelay 500000
mkQuikBroker :: Manager -> FilePath -> FilePath -> [T.Text] -> Maybe (T.Text, T.Text) -> ExceptT T.Text IO BrokerInterface
mkQuikBroker man dllPath quikPath accs tgParams = do
q <- mkQuik dllPath quikPath q <- mkQuik dllPath quikPath
msgChan <- liftIO $ newBoundedChan 100 msgChan <- liftIO $ newBoundedChan 100
msgTid <- liftIO $ case tgParams of
Nothing -> return Nothing
Just (tgToken, chatId) -> do
tgCtx <- mkTelegramContext man tgToken
tid <- forkIO $ messageThread tgCtx chatId msgChan
return $ Just tid
state <- liftIO $ newIORef QuikBrokerState { state <- liftIO $ newIORef QuikBrokerState {
notificationCallback = Nothing, notificationCallback = Nothing,
@ -80,9 +63,7 @@ mkQuikBroker man dllPath quikPath accs tgParams = do
orderMap = M.empty, orderMap = M.empty,
orderIdMap = BM.empty, orderIdMap = BM.empty,
trans2orderid = M.empty, trans2orderid = M.empty,
transIdCounter = 1, transIdCounter = 1
messageChan = msgChan,
messageTid = msgTid
} }
setCallbacks q (qbTransactionCallback state) (qbOrderCallback state) (qbTradeCallback state) setCallbacks q (qbTransactionCallback state) (qbOrderCallback state) (qbTradeCallback state)
@ -222,11 +203,7 @@ qbTradeCallback state quiktrade = do
idMap <- orderIdMap <$> readIORef state idMap <- orderIdMap <$> readIORef state
debugM "Quik" $ "Trade: " ++ show quiktrade debugM "Quik" $ "Trade: " ++ show quiktrade
case BM.lookup (qtOrderId quiktrade) idMap >>= flip M.lookup orders of case BM.lookup (qtOrderId quiktrade) idMap >>= flip M.lookup orders of
Just order -> do Just order -> maybeCall notificationCallback state (TradeNotification $ tradeFor order)
msgChan <- messageChan <$> readIORef state
tryWriteChan msgChan $ TL.toStrict $ format "Trade: {} of {} at {} for account {} ({}/{})"
(show (tradeOperation (tradeFor order)), orderSecurity order, qtPrice quiktrade, orderAccountId order, (strategyId . orderSignalId) order, (signalName . orderSignalId) order)
maybeCall notificationCallback state (TradeNotification $ tradeFor order)
Nothing -> warningM "Quik" $ "Incoming trade for unknown order: " ++ show quiktrade Nothing -> warningM "Quik" $ "Incoming trade for unknown order: " ++ show quiktrade
where where
tradeFor order = Trade { tradeFor order = Trade {

5
src/Broker/QuikBroker/HsQuik.hs

@ -1,5 +0,0 @@
module Broker.QuikBroker.HsQuik (
) where
import Broker.QuikBroker.Trans2QuikApi

9
src/Network/Telegram.hs

@ -18,17 +18,20 @@ import qualified Data.ByteString.UTF8 as BU8
import Data.Aeson import Data.Aeson
import Data.Aeson.Types import Data.Aeson.Types
type TelegramApiToken = T.Text
type TelegramChatId = T.Text
data TelegramContext = TelegramContext { data TelegramContext = TelegramContext {
tgToken :: T.Text, tgToken :: TelegramApiToken,
httpMan :: Manager httpMan :: Manager
} }
mkTelegramContext :: Manager -> T.Text -> IO TelegramContext mkTelegramContext :: Manager -> TelegramApiToken -> IO TelegramContext
mkTelegramContext man token = do mkTelegramContext man token = do
return TelegramContext { httpMan = man, tgToken = token } return TelegramContext { httpMan = man, tgToken = token }
sendMessage :: TelegramContext -> T.Text -> T.Text -> IO () sendMessage :: TelegramContext -> TelegramChatId -> T.Text -> IO ()
sendMessage ctx chatId text = do sendMessage ctx chatId text = do
req <- parseUrl $ "https://api.telegram.org/bot" ++ (T.unpack $ tgToken ctx) ++ "/sendMessage" req <- parseUrl $ "https://api.telegram.org/bot" ++ (T.unpack $ tgToken ctx) ++ "/sendMessage"
void $ withResponse (req { method = "POST", requestHeaders = [("Content-Type", BU8.fromString "application/json")], requestBody = (RequestBodyLBS . encode) (object ["chat_id" .= chatId, "text" .= text]) }) (httpMan ctx) (\resp -> brConsume (responseBody resp)) void $ withResponse (req { method = "POST", requestHeaders = [("Content-Type", BU8.fromString "application/json")], requestBody = (RequestBodyLBS . encode) (object ["chat_id" .= chatId, "text" .= text]) }) (httpMan ctx) (\resp -> brConsume (responseBody resp))

2
stack.yaml

@ -15,7 +15,7 @@
# resolver: # resolver:
# name: custom-snapshot # name: custom-snapshot
# location: "./custom-snapshot.yaml" # location: "./custom-snapshot.yaml"
resolver: lts-7.7 resolver: lts-8.18
# User packages to be built. # User packages to be built.
# Various formats can be used as shown in the example below. # Various formats can be used as shown in the example below.

Loading…
Cancel
Save