Browse Source

Bugfix: backtesting: correct event handling order

stable
Denis Tereshkin 6 years ago
parent
commit
39c2044ba0
  1. 21
      src/ATrade/Driver/Backtest.hs

21
src/ATrade/Driver/Backtest.hs

@ -28,7 +28,7 @@ import ATrade.Types
import Conduit (awaitForever, runConduit, yield, import Conduit (awaitForever, runConduit, yield,
(.|)) (.|))
import Control.Exception.Safe import Control.Exception.Safe
import Control.Lens hiding (ix) import Control.Lens hiding (ix, (<|), (|>))
import Control.Monad.ST (runST) import Control.Monad.ST (runST)
import Control.Monad.State import Control.Monad.State
import Data.Aeson (FromJSON (..), Value (..), decode) import Data.Aeson (FromJSON (..), Value (..), decode)
@ -40,6 +40,8 @@ import Data.List (partition)
import Data.List.Split (splitOn) import Data.List.Split (splitOn)
import qualified Data.Map.Strict as M import qualified Data.Map.Strict as M
import Data.Semigroup ((<>)) import Data.Semigroup ((<>))
import Data.Sequence (Seq (..), (<|), (|>))
import qualified Data.Sequence as Seq
import Data.STRef (newSTRef, readSTRef, writeSTRef) import Data.STRef (newSTRef, readSTRef, writeSTRef)
import qualified Data.Text as T import qualified Data.Text as T
import Data.Text.IO (putStrLn) import Data.Text.IO (putStrLn)
@ -66,7 +68,7 @@ data BacktestState c s = BacktestState {
_robotParams :: c, _robotParams :: c,
_strategyEnvironment :: StrategyEnvironment, _strategyEnvironment :: StrategyEnvironment,
_pendingOrders :: [Order], _pendingOrders :: [Order],
_pendingEvents :: [Event], _pendingEvents :: Seq Event,
_tradesLog :: [Trade], _tradesLog :: [Trade],
_orderIdCounter :: Integer, _orderIdCounter :: Integer,
_pendingTimers :: [UTCTime], _pendingTimers :: [UTCTime],
@ -184,7 +186,7 @@ backtestMain _dataDownloadDelta defaultState initCallback callback = do
handleEvents = do handleEvents = do
events <- use pendingEvents events <- use pendingEvents
case events of case events of
(x:xs) -> do x :<| xs -> do
pendingEvents .= xs pendingEvents .= xs
handleEvent x handleEvent x
handleEvents handleEvents
@ -229,8 +231,8 @@ backtestMain _dataDownloadDelta defaultState initCallback callback = do
ts <- use $ strategyEnvironment . seLastTimestamp ts <- use $ strategyEnvironment . seLastTimestamp
let thisTrade = mkTrade order price ts let thisTrade = mkTrade order price ts
tradesLog %= (\log' -> thisTrade : log') tradesLog %= (\log' -> thisTrade : log')
pendingEvents %= ((:) (OrderUpdate (orderId order) Executed)) pendingEvents %= (\s -> (OrderUpdate (orderId order) Executed) <| s)
pendingEvents %= ((:) (NewTrade thisTrade)) pendingEvents %= (\s -> (NewTrade thisTrade) <| s)
mkTrade :: Order -> Price -> UTCTime -> Trade mkTrade :: Order -> Price -> UTCTime -> Trade
mkTrade order price ts = Trade { mkTrade order price ts = Trade {
@ -249,6 +251,7 @@ backtestMain _dataDownloadDelta defaultState initCallback callback = do
handleEvent event@(NewBar bar) = do handleEvent event@(NewBar bar) = do
executePendingOrders bar executePendingOrders bar
handleEvents -- This should pass OrderUpdate events to the callback before NewBar events
firedTimers <- fireTimers (barTimestamp bar) firedTimers <- fireTimers (barTimestamp bar)
mapM_ (\x -> enqueueEvent (TimerFired x)) firedTimers mapM_ (\x -> enqueueEvent (TimerFired x)) firedTimers
handleEvent' event handleEvent' event
@ -283,14 +286,14 @@ backtestMain _dataDownloadDelta defaultState initCallback callback = do
rowToBar tid r = Bar tid (rowTime r) (rowOpen r) (rowHigh r) (rowLow r) (rowClose r) (rowVolume r) rowToBar tid r = Bar tid (rowTime r) (rowOpen r) (rowHigh r) (rowLow r) (rowClose r) (rowVolume r)
enqueueEvent event = pendingEvents %= ((:) event) enqueueEvent event = pendingEvents %= (\s -> s |> event)
instance (Default c, Default s) => Default (BacktestState c s) instance (Default c, Default s) => Default (BacktestState c s)
where where
def = defaultBacktestState def def [] def = defaultBacktestState def def []
defaultBacktestState :: s -> c -> [Ticker] -> BacktestState c s defaultBacktestState :: s -> c -> [Ticker] -> BacktestState c s
defaultBacktestState s c tickerList = BacktestState 0 s c (StrategyEnvironment "" "" 1 tickers' (UTCTime (fromGregorian 1970 1 1) 0)) [] [] [] 1 [] [] defaultBacktestState s c tickerList = BacktestState 0 s c (StrategyEnvironment "" "" 1 tickers' (UTCTime (fromGregorian 1970 1 1) 0)) [] Seq.empty [] 1 [] []
where where
tickers' = M.fromList $ map (\x -> (code x, BarSeries (code x) (Timeframe (timeframeSeconds x)) [])) tickerList tickers' = M.fromList $ map (\x -> (code x, BarSeries (code x) (Timeframe (timeframeSeconds x)) [])) tickerList
@ -307,14 +310,14 @@ instance MonadRobot (BacktestingMonad c s) c s where
oid <- nextOrderId oid <- nextOrderId
let orderWithId = order { orderId = oid } let orderWithId = order { orderId = oid }
pendingOrders %= ((:) orderWithId) pendingOrders %= ((:) orderWithId)
pendingEvents %= ((:) (OrderSubmitted orderWithId)) pendingEvents %= (\s -> s |> (OrderSubmitted orderWithId))
cancelOrder oid = do cancelOrder oid = do
orders <- use pendingOrders orders <- use pendingOrders
let (matchingOrders, otherOrders) = partition (\o -> orderId o == oid) orders let (matchingOrders, otherOrders) = partition (\o -> orderId o == oid) orders
case matchingOrders of case matchingOrders of
[] -> return () [] -> return ()
xs -> do xs -> do
mapM_ (\o -> pendingEvents %= ((:) (OrderUpdate (orderId o) Cancelled))) xs mapM_ (\o -> pendingEvents %= (\s -> s |> (OrderUpdate (orderId o) Cancelled))) xs
pendingOrders .= otherOrders pendingOrders .= otherOrders
appendToLog txt = logs %= ((:) txt) appendToLog txt = logs %= ((:) txt)
setupTimer time = pendingTimers %= ((:) time) setupTimer time = pendingTimers %= ((:) time)

Loading…
Cancel
Save