5 changed files with 180 additions and 7 deletions
@ -0,0 +1,141 @@
@@ -0,0 +1,141 @@
|
||||
|
||||
module Broker.PaperBroker ( |
||||
PaperBrokerState, |
||||
mkPaperBroker |
||||
) where |
||||
|
||||
import Data.Hashable |
||||
import Data.Bits |
||||
import Control.Concurrent.BoundedChan |
||||
import Data.ATrade |
||||
import Data.IORef |
||||
import qualified Data.HashMap as M |
||||
import Broker |
||||
import Data.Time.Clock |
||||
import Data.Decimal |
||||
import Control.Monad |
||||
import Control.Concurrent hiding (readChan) |
||||
import System.Log.Logger |
||||
|
||||
data TickMapKey = TickMapKey String DataType |
||||
deriving (Show, Eq, Ord) |
||||
|
||||
instance Hashable TickMapKey where |
||||
hashWithSalt salt (TickMapKey s dt) = hashWithSalt salt s `xor` hashWithSalt salt (fromEnum dt) |
||||
|
||||
data PaperBrokerState = PaperBrokerState { |
||||
pbTid :: Maybe ThreadId, |
||||
tickChannel :: BoundedChan Tick, |
||||
tickMap :: M.Map TickMapKey Tick, |
||||
orders :: M.Map OrderId Order, |
||||
cash :: Decimal, |
||||
orderIdCounter :: OrderId, |
||||
tradeCallback :: Maybe (Trade -> IO ()), |
||||
orderCallback :: Maybe (Order -> IO ()) |
||||
} |
||||
|
||||
mkPaperBroker :: BoundedChan Tick -> Decimal -> [String] -> IO Broker |
||||
mkPaperBroker tickChan startCash accounts = do |
||||
state <- newIORef PaperBrokerState { |
||||
pbTid = Nothing, |
||||
tickChannel = tickChan, |
||||
tickMap = M.empty, |
||||
orders = M.empty, |
||||
cash = startCash, |
||||
orderIdCounter = 1, |
||||
tradeCallback = Nothing, |
||||
orderCallback = Nothing } |
||||
|
||||
tid <- forkIO $ brokerThread state |
||||
atomicModifyIORef' state (\s -> (s { pbTid = Just tid }, ()) ) |
||||
|
||||
return Broker { |
||||
accounts = accounts, |
||||
setTradeCallback = pbSetTradeCallback state, |
||||
setOrderCallback = pbSetOrderCallback state, |
||||
submitOrder = pbSubmitOrder state, |
||||
cancelOrder = pbCancelOrder state, |
||||
destroyBroker = pbDestroyBroker state } |
||||
|
||||
brokerThread :: IORef PaperBrokerState -> IO () |
||||
brokerThread state = do |
||||
chan <- tickChannel <$> readIORef state |
||||
forever $ do |
||||
tick <- readChan chan |
||||
atomicModifyIORef' state (\s -> (s { tickMap = M.insert (makeKey tick) tick (tickMap s) }, ()) ) |
||||
where |
||||
makeKey tick = TickMapKey (security tick) (datatype tick) |
||||
|
||||
nextOrderId :: IORef PaperBrokerState -> IO OrderId |
||||
nextOrderId state = do |
||||
id <- orderIdCounter <$> readIORef state |
||||
modifyIORef state (\s -> s { orderIdCounter = id + 1 } ) |
||||
return id |
||||
|
||||
pbSetTradeCallback :: IORef PaperBrokerState -> Maybe (Trade -> IO ()) -> IO() |
||||
pbSetTradeCallback state callback = modifyIORef state (\s -> s { tradeCallback = callback } ) |
||||
|
||||
pbSetOrderCallback :: IORef PaperBrokerState -> Maybe (Order -> IO ()) -> IO() |
||||
pbSetOrderCallback state callback = modifyIORef state (\s -> s { orderCallback = callback } ) |
||||
|
||||
pbSubmitOrder :: IORef PaperBrokerState -> Order -> IO OrderId |
||||
pbSubmitOrder state order = do |
||||
curState <- readIORef state |
||||
case orderPrice order of |
||||
Market -> executeMarketOrder state order |
||||
Limit price -> submitLimitOrder state order |
||||
Stop price trigger -> submitStopOrder state order |
||||
|
||||
where |
||||
executeMarketOrder state order = do |
||||
tm <- tickMap <$> readIORef state |
||||
oid <- nextOrderId state |
||||
case M.lookup key tm of |
||||
Nothing -> let newOrder = order { orderState = Error "Unable to execute order: no bid/ask", orderId = oid } in |
||||
atomicModifyIORef' state (\s -> (s { orders = M.insert oid newOrder $ orders s }, ()) ) |
||||
|
||||
Just tick -> let newOrder = order { orderState = Executed, orderId = oid } |
||||
tradeVolume = (realFracToDecimal 10 (fromIntegral $ orderQuantity order) * value tick) in do |
||||
atomicModifyIORef' state (\s -> (s { orders = M.insert oid newOrder $ orders s , cash = cash s - tradeVolume}, ()) ) |
||||
ts <- getCurrentTime |
||||
maybeCall tradeCallback state $ mkTrade tick order ts |
||||
|
||||
return oid |
||||
|
||||
submitLimitOrder = undefined |
||||
submitStopOrder = undefined |
||||
|
||||
orderDatatype order = case orderOperation order of |
||||
Buy -> BestOffer |
||||
Sell -> BestBid |
||||
|
||||
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", |
||||
tradeAccount = orderAccountId order, |
||||
tradeSecurity = orderSecurity order, |
||||
tradeTimestamp = timestamp, |
||||
tradeSignalId = orderSignalId order } |
||||
|
||||
|
||||
pbCancelOrder :: IORef PaperBrokerState -> OrderId -> IO () |
||||
pbCancelOrder state order = undefined |
||||
|
||||
pbDestroyBroker :: IORef PaperBrokerState -> IO () |
||||
pbDestroyBroker state = do |
||||
maybeTid <- pbTid <$> readIORef state |
||||
case maybeTid of |
||||
Just tid -> killThread tid |
||||
Nothing -> return () |
||||
|
||||
Loading…
Reference in new issue