Browse Source

Types encode/decode

master
Denis Tereshkin 8 years ago
parent
commit
952e7fa113
  1. 5
      libatrade.cabal
  2. 29
      src/ATrade/Price.hs
  3. 55
      src/ATrade/Types.hs
  4. 4
      test/ArbitraryInstances.hs
  5. 1
      test/TestBrokerProtocol.hs
  6. 1
      test/TestTypes.hs

5
libatrade.cabal

@ -1,5 +1,5 @@
name: libatrade name: libatrade
version: 0.2.0.0 version: 0.3.0.0
synopsis: ATrade infrastructure core library synopsis: ATrade infrastructure core library
description: Please see README.md description: Please see README.md
homepage: https://github.com/asakul/libatrade.git homepage: https://github.com/asakul/libatrade.git
@ -27,7 +27,6 @@ library
, ATrade.Broker.TradeSinks.ZMQTradeSink , ATrade.Broker.TradeSinks.ZMQTradeSink
, ATrade.Util , ATrade.Util
build-depends: base >= 4.7 && < 5 build-depends: base >= 4.7 && < 5
, Decimal
, time , time
, datetime , datetime
, bytestring , bytestring
@ -52,6 +51,7 @@ library
, http-client , http-client
, http-client-tls , http-client-tls
, utf8-string , utf8-string
, scientific
default-language: Haskell2010 default-language: Haskell2010
executable libatrade-exe executable libatrade-exe
@ -77,7 +77,6 @@ test-suite libatrade-test
, tasty-hspec , tasty-hspec
, quickcheck-text , quickcheck-text
, quickcheck-instances , quickcheck-instances
, Decimal
, scientific , scientific
, tuple , tuple
, time , time

29
src/ATrade/Price.hs

@ -5,10 +5,16 @@ module ATrade.Price (
fromDouble, fromDouble,
toDouble, toDouble,
decompose, decompose,
compose compose,
fromScientific,
toScientific
) where ) where
import Data.Int import Data.Int
import Data.Ratio
import Data.Aeson
import Data.Scientific
data Price = Price { data Price = Price {
priceQuants :: !Int64 priceQuants :: !Int64
@ -41,9 +47,30 @@ toDouble p = fromIntegral (priceQuants p) / fromIntegral mega
fromDouble :: Double -> Price fromDouble :: Double -> Price
fromDouble d = Price { priceQuants = truncate (d * fromIntegral mega) } fromDouble d = Price { priceQuants = truncate (d * fromIntegral mega) }
toScientific :: Price -> Scientific
toScientific p = normalize $ scientific (toInteger $ priceQuants p) (-6)
fromScientific :: Scientific -> Price
fromScientific d = Price { priceQuants = if base10Exponent nd >= -6 then fromInteger $ coefficient nd * (10 ^ (base10Exponent nd + 6)) else 0 }
where
nd = normalize d
decompose :: Price -> (Int64, Int32) decompose :: Price -> (Int64, Int32)
decompose Price{priceQuants = p} = (p `div` mega, (fromInteger . toInteger) $ p `mod` mega) decompose Price{priceQuants = p} = (p `div` mega, (fromInteger . toInteger) $ p `mod` mega)
compose :: (Int64, Int32) -> Price compose :: (Int64, Int32) -> Price
compose (int, frac) = Price { priceQuants = int * mega + (fromInteger . toInteger) frac } compose (int, frac) = Price { priceQuants = int * mega + (fromInteger . toInteger) frac }
instance FromJSON Price where
parseJSON = withScientific "number" (\x -> let nx = normalize x in
return Price { priceQuants = if base10Exponent nx >= -6 then fromInteger $ coefficient nx * (10 ^ (base10Exponent nx + 6)) else 0 })
instance ToJSON Price where
toJSON x = Number (normalize $ scientific (toInteger $ priceQuants x) (-6))
instance Real Price where
toRational a = (toInteger . priceQuants $ a) % toInteger mega
instance Fractional Price where
fromRational a = fromInteger (numerator a) / fromInteger (denominator a)
a / b = fromDouble $ toDouble a / toDouble b

55
src/ATrade/Types.hs

@ -36,7 +36,6 @@ import Data.Binary.Builder
import Data.Binary.Get import Data.Binary.Get
import Data.ByteString.Lazy as B import Data.ByteString.Lazy as B
import Data.DateTime import Data.DateTime
import Data.Decimal
import Data.Int import Data.Int
import Data.List as L import Data.List as L
import Data.Maybe import Data.Maybe
@ -138,14 +137,6 @@ parseTick = do
makeTimestamp :: Word64 -> Word32 -> UTCTime makeTimestamp :: Word64 -> Word32 -> UTCTime
makeTimestamp sec usec = addUTCTime (fromRational $ toInteger usec % 1000000) (fromSeconds . toInteger $ sec) makeTimestamp sec usec = addUTCTime (fromRational $ toInteger usec % 1000000) (fromSeconds . toInteger $ sec)
makeValue :: Int64 -> Int32 -> Decimal
makeValue intpart nanopart = case eitherFromRational r of
Right v -> v + convertedIntPart
Left _ -> convertedIntPart
where
convertedIntPart = realFracToDecimal 10 (fromIntegral intpart)
r = toInteger nanopart % 1000000000
deserializeTick :: [ByteString] -> Maybe Tick deserializeTick :: [ByteString] -> Maybe Tick
deserializeTick (header:rawData:_) = case runGetOrFail parseTick rawData of deserializeTick (header:rawData:_) = case runGetOrFail parseTick rawData of
Left (_, _, _) -> Nothing Left (_, _, _) -> Nothing
@ -161,10 +152,10 @@ deserializeTickBody bs = case runGetOrFail parseTick bs of
data Bar = Bar { data Bar = Bar {
barSecurity :: !TickerId, barSecurity :: !TickerId,
barTimestamp :: !UTCTime, barTimestamp :: !UTCTime,
barOpen :: !Decimal, barOpen :: !Price,
barHigh :: !Decimal, barHigh :: !Price,
barLow :: !Decimal, barLow :: !Price,
barClose :: !Decimal, barClose :: !Price,
barVolume :: !Integer barVolume :: !Integer
} deriving (Show, Eq, Generic) } deriving (Show, Eq, Generic)
@ -186,30 +177,28 @@ instance ToJSON SignalId where
"signal-name" .= signalName sid, "signal-name" .= signalName sid,
"comment" .= comment sid ] "comment" .= comment sid ]
instance FromJSON Decimal where data OrderPrice = Market | Limit Price | Stop Price Price | StopMarket Price
parseJSON = withScientific "number" (return . realFracToDecimal 10 . toRational)
instance ToJSON Decimal where
toJSON = Number . fromRational . toRational
data OrderPrice = Market | Limit Decimal | Stop Decimal Decimal | StopMarket Decimal
deriving (Show, Eq) deriving (Show, Eq)
decimal :: (RealFrac r) => r -> Decimal
decimal = realFracToDecimal 10
instance FromJSON OrderPrice where instance FromJSON OrderPrice where
parseJSON (String s) = when (s /= "market") (fail "If string, then should be 'market'") >> parseJSON (String s) = when (s /= "market") (fail "If string, then should be 'market'") >>
return Market return Market
parseJSON (Number n) = return $ Limit $ decimal n parseJSON a@(Number n) = do
price <- parseJSON a
return $ Limit price
parseJSON (Object v) = do parseJSON (Object v) = do
triggerPrice <- v .: "trigger" :: Parser Double triggerPrice <- v .: "trigger"
execPrice <- v .: "execution" case triggerPrice of
case execPrice of a@(Number trpr) -> do
(String s) -> when (s /= "market") (fail "If string, then should be 'market'") >> return $ StopMarket (decimal triggerPrice) trprice <- parseJSON a
(Number n) -> return $ Stop (decimal triggerPrice) (decimal n) execPrice <- v .: "execution"
_ -> fail "Should be either number or 'market'" case execPrice of
(String s) -> if s /= "market"
then (fail "If string, then should be 'market'")
else return $ StopMarket trprice
(Number n) -> return $ Stop trprice (fromScientific n)
_ -> fail "Should be either number or 'market'"
parseJSON _ = fail "OrderPrice" parseJSON _ = fail "OrderPrice"
@ -321,9 +310,9 @@ instance ToJSON Order where
data Trade = Trade { data Trade = Trade {
tradeOrderId :: OrderId, tradeOrderId :: OrderId,
tradePrice :: Decimal, tradePrice :: Price,
tradeQuantity :: Integer, tradeQuantity :: Integer,
tradeVolume :: Decimal, tradeVolume :: Price,
tradeVolumeCurrency :: T.Text, tradeVolumeCurrency :: T.Text,
tradeOperation :: Operation, tradeOperation :: Operation,
tradeAccount :: T.Text, tradeAccount :: T.Text,
@ -384,6 +373,6 @@ data TickerInfo = TickerInfo {
tiClass :: T.Text, tiClass :: T.Text,
tiBase :: Maybe TickerId, tiBase :: Maybe TickerId,
tiLotSize :: Integer, tiLotSize :: Integer,
tiTickSize :: Decimal tiTickSize :: Price
} deriving (Show, Eq) } deriving (Show, Eq)

4
test/ArbitraryInstances.hs

@ -15,7 +15,6 @@ import ATrade.Price as P
import ATrade.Broker.Protocol import ATrade.Broker.Protocol
import Data.Int import Data.Int
import Data.Decimal
import Data.Scientific import Data.Scientific
import Data.Time.Clock import Data.Time.Clock
import Data.Time.Calendar import Data.Time.Calendar
@ -42,9 +41,6 @@ instance Arbitrary Tick where
instance Arbitrary DataType where instance Arbitrary DataType where
arbitrary = toEnum <$> choose (1, 10) arbitrary = toEnum <$> choose (1, 10)
instance Arbitrary Decimal where
arbitrary = realFracToDecimal 10 <$> (arbitrary :: Gen Scientific)
instance Arbitrary SignalId where instance Arbitrary SignalId where
arbitrary = SignalId <$> arbitrary <*> arbitrary <*> arbitrary arbitrary = SignalId <$> arbitrary <*> arbitrary <*> arbitrary

1
test/TestBrokerProtocol.hs

@ -15,7 +15,6 @@ import ATrade.Broker.Protocol
import ArbitraryInstances import ArbitraryInstances
import Data.Aeson import Data.Aeson
import Data.Decimal
import Data.Scientific import Data.Scientific
properties = testGroup "Broker.Protocol" [ properties = testGroup "Broker.Protocol" [

1
test/TestTypes.hs

@ -16,7 +16,6 @@ import ArbitraryInstances
import Data.Aeson import Data.Aeson
import Data.Aeson.Types import Data.Aeson.Types
import Data.Decimal
import Data.Scientific import Data.Scientific
import Data.Text import Data.Text
import Data.Time.Calendar import Data.Time.Calendar

Loading…
Cancel
Save