Browse Source

More tests for BarAggregator

master
Denis Tereshkin 7 years ago
parent
commit
cb6230ecd5
  1. 2
      robocom-zero.cabal
  2. 9
      src/ATrade/BarAggregator.hs
  3. 4
      src/ATrade/Driver/Real.hs
  4. 111
      test/ArbitraryInstances.hs
  5. 7
      test/Spec.hs
  6. 37
      test/Test/BarAggregator.hs

2
robocom-zero.cabal

@ -88,12 +88,14 @@ test-suite robots-test
, quickcheck-text , quickcheck-text
, quickcheck-instances , quickcheck-instances
, containers , containers
, safe
ghc-options: -threaded -rtsopts -with-rtsopts=-N ghc-options: -threaded -rtsopts -with-rtsopts=-N
default-language: Haskell2010 default-language: Haskell2010
other-modules: Test.RoboCom.Indicators other-modules: Test.RoboCom.Indicators
, Test.RoboCom.Positions , Test.RoboCom.Positions
, Test.RoboCom.Utils , Test.RoboCom.Utils
, Test.BarAggregator , Test.BarAggregator
, ArbitraryInstances
source-repository head source-repository head
type: git type: git

9
src/ATrade/BarAggregator.hs

@ -17,6 +17,7 @@ module ATrade.BarAggregator (
lLastTicks, lLastTicks,
BarAggregator(..), BarAggregator(..),
mkAggregatorFromBars, mkAggregatorFromBars,
handleTicks,
handleTick, handleTick,
handleBar, handleBar,
hmsToDiffTime hmsToDiffTime
@ -54,6 +55,14 @@ lLastTicks = lens lastTicks (\s b -> s { lastTicks = b })
hmsToDiffTime :: Int -> Int -> Int -> DiffTime hmsToDiffTime :: Int -> Int -> Int -> DiffTime
hmsToDiffTime h m s = secondsToDiffTime $ toInteger $ h * 3600 + m * 60 + s hmsToDiffTime h m s = secondsToDiffTime $ toInteger $ h * 3600 + m * 60 + s
handleTicks :: [Tick] -> BarAggregator -> ([Bar], BarAggregator)
handleTicks ticks aggregator = foldl f ([], aggregator) ticks
where
f (bars', agg) tick = let (mbar, agg') = handleTick tick agg in
case mbar of
Just bar -> (bar : bars', agg')
_ -> (bars', agg')
-- | main logic of bar aggregator -- | main logic of bar aggregator
handleTick :: Tick -> BarAggregator -> (Maybe Bar, BarAggregator) handleTick :: Tick -> BarAggregator -> (Maybe Bar, BarAggregator)
handleTick tick = runState $ do handleTick tick = runState $ do

4
src/ATrade/Driver/Real.hs

@ -225,6 +225,7 @@ robotMain dataDownloadDelta defaultState initCallback callback = do
loadStrategyTimers params = case redisSocket params of loadStrategyTimers params = case redisSocket params of
Nothing -> return [] Nothing -> return []
Just sock -> do Just sock -> do
#ifdef linux_HOST_OS
conn <- checkedConnect $ defaultConnectInfo { connectPort = UnixSocket sock } conn <- checkedConnect $ defaultConnectInfo { connectPort = UnixSocket sock }
res <- runRedis conn $ get (encodeUtf8 $ T.pack $ instanceId params ++ "timers") res <- runRedis conn $ get (encodeUtf8 $ T.pack $ instanceId params ++ "timers")
case res of case res of
@ -240,6 +241,9 @@ robotMain dataDownloadDelta defaultState initCallback callback = do
Nothing -> do Nothing -> do
warningM "main" "Unable to load state" warningM "main" "Unable to load state"
return [] return []
#else
error "Not implemented"
#endif
loadStrategyState params = case redisSocket params of loadStrategyState params = case redisSocket params of

111
test/ArbitraryInstances.hs

@ -0,0 +1,111 @@
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeSynonymInstances #-}
module ArbitraryInstances (
) where
import Test.QuickCheck.Instances ()
import Test.Tasty.QuickCheck as QC
import ATrade.Price as P
import ATrade.Types
import qualified Data.Text as T
import Data.Time.Calendar
import Data.Time.Clock
notTooBig :: (Num a, Ord a) => a -> Bool
notTooBig x = abs x < 100000000
arbitraryTickerId = arbitrary `suchThat` (\t -> T.all (/= ':') t && t /= "")
instance Arbitrary Tick where
arbitrary = Tick <$>
arbitraryTickerId <*>
arbitrary <*>
arbitraryTimestamp <*>
arbitrary <*>
arbitrary
arbitraryTimestamp = do
y <- choose (1970, 2050)
m <- choose (1, 12)
d <- choose (1, 31)
sec <- secondsToDiffTime <$> choose (0, 86399)
return $ UTCTime (fromGregorian y m d) sec
instance Arbitrary DataType where
arbitrary = toEnum <$> choose (1, 10)
instance Arbitrary SignalId where
arbitrary = SignalId <$> arbitrary <*> arbitrary <*> arbitrary
instance Arbitrary OrderPrice where
arbitrary = do
v <- choose (1, 4) :: Gen Int
if | v == 1 -> return Market
| v == 2 -> Limit <$> arbitrary `suchThat` notTooBig
| v == 3 -> Stop <$> arbitrary `suchThat` notTooBig <*> arbitrary `suchThat` notTooBig
| v == 4 -> StopMarket <$> arbitrary `suchThat` notTooBig
| otherwise -> fail "Invalid case"
instance Arbitrary Operation where
arbitrary = elements [Buy, Sell]
instance Arbitrary OrderState where
arbitrary = elements [Unsubmitted,
Submitted,
PartiallyExecuted,
Executed,
Cancelled,
Rejected,
OrderError ]
instance Arbitrary Order where
arbitrary = Order <$>
arbitrary <*>
arbitrary <*>
arbitrary <*>
arbitrary <*>
arbitrary <*>
arbitrary <*>
arbitrary <*>
arbitrary <*>
arbitrary
instance Arbitrary Trade where
arbitrary = Trade <$>
arbitrary <*>
arbitrary <*>
arbitrary <*>
arbitrary <*>
arbitrary <*>
arbitrary <*>
arbitrary <*>
arbitrary <*>
arbitrary <*>
arbitrary <*>
arbitrary
instance Arbitrary P.Price where
arbitrary = P.Price <$> (arbitrary `suchThat` (\p -> abs p < 1000000000 * 10000000))
instance Arbitrary Bar where
arbitrary = Bar <$>
arbitraryTickerId <*>
arbitraryTimestamp <*>
arbitrary <*>
arbitrary <*>
arbitrary <*>
arbitrary <*>
arbitrary `suchThat` (> 0)
instance Arbitrary BarTimeframe where
arbitrary = BarTimeframe <$> (arbitrary `suchThat` (\p -> p > 0 && p < 86400 * 365))

7
test/Spec.hs

@ -6,7 +6,7 @@ import qualified Test.RoboCom.Utils
import Test.Tasty import Test.Tasty
main :: IO () main :: IO ()
main = defaultMain $ testGroup "Tests" [unitTests] main = defaultMain $ testGroup "Tests" [unitTests, properties]
unitTests :: TestTree unitTests :: TestTree
unitTests = testGroup "Unit Tests" unitTests = testGroup "Unit Tests"
@ -14,3 +14,8 @@ unitTests = testGroup "Unit Tests"
Test.RoboCom.Positions.unitTests, Test.RoboCom.Positions.unitTests,
Test.RoboCom.Utils.unitTests, Test.RoboCom.Utils.unitTests,
Test.BarAggregator.unitTests ] Test.BarAggregator.unitTests ]
properties :: TestTree
properties = testGroup "Properties"
[Test.BarAggregator.properties ]

37
test/Test/BarAggregator.hs

@ -2,22 +2,28 @@
module Test.BarAggregator module Test.BarAggregator
( (
unitTests unitTests,
properties
) where ) where
import ATrade.BarAggregator import ATrade.BarAggregator
import ATrade.RoboCom.Types import ATrade.RoboCom.Types
import ATrade.Types import ATrade.Types
import Data.List
import qualified Data.Map.Strict as M import qualified Data.Map.Strict as M
import qualified Data.Text as T import qualified Data.Text as T
import Data.Time.Calendar import Data.Time.Calendar
import Data.Time.Clock import Data.Time.Clock
import Safe
import Test.Tasty import Test.Tasty
import Test.Tasty.HUnit import Test.Tasty.HUnit
import Test.Tasty.QuickCheck as QC import Test.Tasty.QuickCheck as QC
import Test.Tasty.SmallCheck as SC import Test.Tasty.SmallCheck as SC
import ArbitraryInstances
unitTests = testGroup "BarAggregator" [ unitTests = testGroup "BarAggregator" [
testUnknownBarSeries testUnknownBarSeries
, testOneTick , testOneTick
@ -29,6 +35,10 @@ unitTests = testGroup "BarAggregator" [
, testNextBarAfterBarClose , testNextBarAfterBarClose
] ]
properties = testGroup "BarAggregator" [
prop_allTicksInOneBar
]
testUnknownBarSeries :: TestTree testUnknownBarSeries :: TestTree
testUnknownBarSeries = testCase "Tick with unknown ticker id" $ do testUnknownBarSeries = testCase "Tick with unknown ticker id" $ do
let agg = BarAggregator M.empty M.empty [(0, 86400)] let agg = BarAggregator M.empty M.empty [(0, 86400)]
@ -181,3 +191,28 @@ testNextBarAfterBarClose = testCase "Three bars (smaller timeframe) - next bar a
barLow = fromDouble l, barLow = fromDouble l,
barClose = fromDouble c, barClose = fromDouble c,
barVolume = v } barVolume = v }
prop_allTicksInOneBar :: TestTree
prop_allTicksInOneBar = QC.testProperty "All ticks in one bar" $ QC.forAll (QC.choose (1, 86400)) $ \timeframe ->
QC.forAll (QC.listOf1 (genTick "TEST_TICKER" baseTime timeframe)) $ \ticks ->
let ticks' = sortOn timestamp ticks in
let (newbars, agg) = handleTicks ticks' (mkAggregator "TEST_TICKER" timeframe) in
null newbars &&
((barHigh <$> currentBar "TEST_TICKER" agg) == Just (maximum $ value <$> ticks)) &&
((barLow <$> currentBar "TEST_TICKER" agg) == Just (minimum $ value <$> ticks)) &&
((barOpen <$> currentBar "TEST_TICKER" agg) == (value <$> headMay ticks')) &&
((barClose <$> currentBar "TEST_TICKER" agg) == (value <$> lastMay ticks')) &&
((barVolume <$> currentBar "TEST_TICKER" agg) == Just (sum $ volume <$> ticks))
where
genTick :: T.Text -> UTCTime -> Integer -> Gen Tick
genTick tickerId base tf = do
difftime <- fromRational . toRational . picosecondsToDiffTime <$> choose (0, truncate 1e12 * tf)
val <- arbitrary
vol <- arbitrary `suchThat` (> 0)
return $ Tick tickerId LastTradePrice (difftime `addUTCTime` baseTime) val vol
mkAggregator tickerId tf = mkAggregatorFromBars (M.singleton tickerId (BarSeries tickerId (Timeframe tf) [])) [(0, 86400)]
currentBar tickerId agg = headMay =<< (bsBars <$> M.lookup tickerId (bars agg))
baseTime = UTCTime (fromGregorian 1970 1 1) 0

Loading…
Cancel
Save