Browse Source

PaperBroker: limit orders support

master
Denis Tereshkin 9 years ago
parent
commit
a13c38dbad
  1. 117
      src/Broker/PaperBroker.hs
  2. 3
      src/Broker/QuikBroker.hs

117
src/Broker/PaperBroker.hs

@ -12,12 +12,14 @@ import Data.Hashable
import Data.Bits import Data.Bits
import ATrade.Types import ATrade.Types
import Data.IORef import Data.IORef
import qualified Data.HashMap.Strict as M import qualified Data.List as L
import qualified Data.Map.Strict as M
import qualified Data.Text as T import qualified Data.Text as T
import ATrade.Broker.Protocol import ATrade.Broker.Protocol
import ATrade.Broker.Server import ATrade.Broker.Server
import Data.Time.Clock import Data.Time.Clock
import Data.Decimal import Data.Decimal
import Data.Maybe
import Control.Monad import Control.Monad
import Control.Concurrent.BoundedChan import Control.Concurrent.BoundedChan
import Control.Concurrent hiding (readChan) import Control.Concurrent hiding (readChan)
@ -31,10 +33,11 @@ instance Hashable TickMapKey where
data PaperBrokerState = PaperBrokerState { data PaperBrokerState = PaperBrokerState {
pbTid :: Maybe ThreadId, pbTid :: Maybe ThreadId,
tickMap :: M.HashMap TickMapKey Tick, tickMap :: M.Map TickMapKey Tick,
orders :: M.HashMap OrderId Order, orders :: M.Map OrderId Order,
cash :: ! Decimal, cash :: ! Decimal,
notificationCallback :: Maybe (Notification -> IO ()) notificationCallback :: Maybe (Notification -> IO ()),
pendingOrders :: [Order]
} }
mkPaperBroker :: BoundedChan Tick -> Decimal -> [T.Text] -> IO BrokerInterface mkPaperBroker :: BoundedChan Tick -> Decimal -> [T.Text] -> IO BrokerInterface
@ -44,7 +47,8 @@ mkPaperBroker tickChan startCash accounts = do
tickMap = M.empty, tickMap = M.empty,
orders = M.empty, orders = M.empty,
cash = startCash, cash = startCash,
notificationCallback = Nothing } notificationCallback = Nothing,
pendingOrders = [] }
tid <- forkIO $ brokerThread tickChan state tid <- forkIO $ brokerThread tickChan state
atomicModifyIORef' state (\s -> (s { pbTid = Just tid }, ())) atomicModifyIORef' state (\s -> (s { pbTid = Just tid }, ()))
@ -60,19 +64,72 @@ brokerThread :: BoundedChan Tick -> IORef PaperBrokerState -> IO ()
brokerThread chan state = forever $ do brokerThread chan state = forever $ do
tick <- readChan chan tick <- readChan chan
atomicModifyIORef' state (\s -> (s { tickMap = M.insert (makeKey tick) tick $! tickMap s }, ())) atomicModifyIORef' state (\s -> (s { tickMap = M.insert (makeKey tick) tick $! tickMap s }, ()))
executePendingOrders tick state
where where
makeKey !tick = TickMapKey (security $! tick) (datatype tick) makeKey !tick = TickMapKey (security $! tick) (datatype tick)
executePendingOrders tick state = do
po <- pendingOrders <$> readIORef state
executedIds <- catMaybes <$> mapM execute po
atomicModifyIORef' state (\s -> (s { pendingOrders = L.filter (\order -> orderId order `L.notElem` executedIds) (pendingOrders s)}, ()))
where
execute order =
case orderPrice order of
Market -> do
executeAtTick state order tick
return $ Just $ orderId order
Limit price -> executeLimitAt price order
_ -> return Nothing
executeLimitAt price order = case orderOperation order of
Buy -> if (datatype tick == Price && price > value tick) || (datatype tick == BestOffer && price > value tick)
then do
executeAtTick state order $ tick { value = price }
return $ Just $ orderId order
else return Nothing
Sell -> if (datatype tick == Price && price < value tick) || (datatype tick == BestBid && price < value tick)
then do
executeAtTick state order $ tick { value = price }
return $ Just $ orderId order
else return Nothing
pbSetNotificationCallback :: IORef PaperBrokerState -> Maybe (Notification -> IO ()) -> IO() pbSetNotificationCallback :: IORef PaperBrokerState -> Maybe (Notification -> IO ()) -> IO()
pbSetNotificationCallback state callback = atomicModifyIORef' state (\s -> (s { notificationCallback = callback }, ()) ) pbSetNotificationCallback state callback = atomicModifyIORef' state (\s -> (s { notificationCallback = callback }, ()) )
mkTrade :: Tick -> Order -> UTCTime -> Trade
mkTrade tick order timestamp = Trade {
tradeOrderId = orderId order,
tradePrice = value tick,
tradeQuantity = orderQuantity order,
tradeVolume = realFracToDecimal 10 (fromIntegral $ orderQuantity order) * value tick,
tradeVolumeCurrency = "TEST",
tradeOperation = orderOperation order,
tradeAccount = orderAccountId order,
tradeSecurity = orderSecurity order,
tradeTimestamp = timestamp,
tradeSignalId = orderSignalId order }
maybeCall proj state arg = do
cb <- proj <$> readIORef state
case cb of
Just callback -> callback arg
Nothing -> return ()
executeAtTick state order tick = do
let newOrder = order { orderState = Executed }
let tradeVolume = realFracToDecimal 10 (fromIntegral $ orderQuantity order) * value tick
atomicModifyIORef' state (\s -> (s { orders = M.insert (orderId order) newOrder $ orders s , cash = cash s - tradeVolume}, ()))
debugM "PaperBroker" $ "Executed: " ++ show newOrder
ts <- getCurrentTime
maybeCall notificationCallback state $ TradeNotification $ mkTrade tick order ts
maybeCall notificationCallback state $ OrderNotification (orderId order) Executed
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
case orderPrice order of case orderPrice order of
Market -> executeMarketOrder state order Market -> executeMarketOrder state order
Limit price -> submitLimitOrder state order Limit price -> submitLimitOrder price state order
Stop price trigger -> submitStopOrder state order Stop price trigger -> submitStopOrder state order
StopMarket trigger -> submitStopMarketOrder state order StopMarket trigger -> submitStopMarketOrder state order
@ -83,15 +140,22 @@ pbSubmitOrder state order = do
Nothing -> let newOrder = order { orderState = OrderError } in Nothing -> let newOrder = order { orderState = OrderError } in
atomicModifyIORef' state (\s -> (s { orders = M.insert (orderId order) newOrder $ orders s }, ())) atomicModifyIORef' state (\s -> (s { orders = M.insert (orderId order) newOrder $ orders s }, ()))
Just tick -> let newOrder = order { orderState = Executed } Just tick -> executeAtTick state order tick
tradeVolume = (realFracToDecimal 10 (fromIntegral $ orderQuantity order) * value tick) in do submitLimitOrder price state order = do
atomicModifyIORef' state (\s -> (s { orders = M.insert (orderId order) newOrder $ orders s , cash = cash s - tradeVolume}, ())) tm <- tickMap <$> readIORef state
debugM "PaperBroker" $ "Executed: " ++ show newOrder case M.lookup key tm of
ts <- getCurrentTime Nothing -> do
maybeCall notificationCallback state $ TradeNotification $ mkTrade tick order ts let newOrder = order { orderState = Submitted }
maybeCall notificationCallback state $ OrderNotification (orderId order) Executed atomicModifyIORef' state (\s -> (s { orders = M.insert (orderId order) newOrder $ orders s }, ()))
maybeCall notificationCallback state $ OrderNotification (orderId order) Submitted
Just tick ->
if ((orderOperation order == Buy) && (value tick < price)) || ((orderOperation order == Sell) && (value tick > price))
then executeAtTick state order tick
else do
let newOrder = order { orderState = Submitted }
atomicModifyIORef' state (\s -> (s { orders = M.insert (orderId order) newOrder $ orders s , pendingOrders = newOrder : pendingOrders s}, ()))
maybeCall notificationCallback state $ OrderNotification (orderId order) Submitted
submitLimitOrder state order = warningM "PaperBroker" $ "Not implemented: Submitted order: " ++ show order
submitStopOrder state order = warningM "PaperBroker" $ "Not implemented: Submitted order: " ++ show order submitStopOrder state order = warningM "PaperBroker" $ "Not implemented: Submitted order: " ++ show order
submitStopMarketOrder state order = warningM "PaperBroker" $ "Not implemented: Submitted order: " ++ show order submitStopMarketOrder state order = warningM "PaperBroker" $ "Not implemented: Submitted order: " ++ show order
@ -100,28 +164,13 @@ pbSubmitOrder state order = do
Sell -> BestBid Sell -> BestBid
key = TickMapKey (orderSecurity order) (orderDatatype order) key = TickMapKey (orderSecurity order) (orderDatatype order)
maybeCall proj state arg = do
cb <- proj <$> readIORef state
case cb of
Just callback -> callback arg
Nothing -> return ()
mkTrade :: Tick -> Order -> UTCTime -> Trade
mkTrade tick order timestamp = Trade {
tradeOrderId = orderId order,
tradePrice = value tick,
tradeQuantity = orderQuantity order,
tradeVolume = realFracToDecimal 10 (fromIntegral $ orderQuantity order) * value tick,
tradeVolumeCurrency = "TEST",
tradeOperation = orderOperation order,
tradeAccount = orderAccountId order,
tradeSecurity = orderSecurity order,
tradeTimestamp = timestamp,
tradeSignalId = orderSignalId order }
pbCancelOrder :: IORef PaperBrokerState -> OrderId -> IO Bool pbCancelOrder :: IORef PaperBrokerState -> OrderId -> IO Bool
pbCancelOrder state order = undefined pbCancelOrder state oid = do
atomicModifyIORef' state (\s -> (s { pendingOrders = L.filter (\o -> orderId o /= oid) (pendingOrders s),
orders = M.adjustWithKey (\k v -> v { orderState = Cancelled }) oid (orders s) }, ()))
maybeCall notificationCallback state $ OrderNotification oid Cancelled
return True
pbDestroyBroker :: IORef PaperBrokerState -> IO () pbDestroyBroker :: IORef PaperBrokerState -> IO ()
pbDestroyBroker state = do pbDestroyBroker state = do

3
src/Broker/QuikBroker.hs

@ -224,7 +224,8 @@ qbTradeCallback state quiktrade = do
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 -> do
msgChan <- messageChan <$> readIORef state msgChan <- messageChan <$> readIORef state
tryWriteChan msgChan $ TL.toStrict $ format "Trade: {} of {} at {} for account {}" (show (tradeOperation (tradeFor order)), orderSecurity order, qtPrice quiktrade, orderAccountId order) 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) 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

Loading…
Cancel
Save