From 86c85d68227a6c46470f0dc6417fd84271738ced Mon Sep 17 00:00:00 2001 From: Boyd Stephen Smith Jr Date: Sat, 23 Nov 2013 17:09:45 -0600 Subject: [PATCH] Orderdata representation. --- Cryptsy/API/Public.hs | 17 +++++++-- Cryptsy/API/Public/Market.hs | 25 +++++-------- Cryptsy/API/Public/OrderBook.hs | 81 +++++++++++++++++++++++++++++++++++++++++ Cryptsy/API/Public/OrderData.hs | 54 +++++++++++++++++++++++++++ Main.hs | 1 + 5 files changed, 158 insertions(+), 20 deletions(-) create mode 100644 Cryptsy/API/Public/OrderBook.hs create mode 100644 Cryptsy/API/Public/OrderData.hs diff --git a/Cryptsy/API/Public.hs b/Cryptsy/API/Public.hs index ea4cac4..3e8aed6 100644 --- a/Cryptsy/API/Public.hs +++ b/Cryptsy/API/Public.hs @@ -1,10 +1,10 @@ {-# LANGUAGE ViewPatterns, GeneralizedNewtypeDeriving #-} module Cryptsy.API.Public ( CryptsyError(..), CryptsyNum(..), E8, PubCryptsy - , parseCryptsyNum, pubCryptsy, pubURL + , parseCryptsyNum, pubCryptsy, pubURL, withNullableArray , module Network.HTTP.Base -- |Re-exported from Data.Aeson - , Value + , Value, Array -- |Re-exported from Data.Aeson.Types , Parser -- |Re-exported from Data.Fixed @@ -20,8 +20,8 @@ import Network.HTTP.Base (Response(..)) import Network.TCP (HandleStream) -- aeson -import Data.Aeson (FromJSON(..), Value(Object), json, withText) -import Data.Aeson.Types (Parser) +import Data.Aeson (FromJSON(..), Value(Array, Object, Null), Array, json, withText) +import Data.Aeson.Types (Parser, typeMismatch) -- attoparsec import Data.Attoparsec.ByteString.Lazy (eitherResult, parse) @@ -29,6 +29,7 @@ import Data.Attoparsec.ByteString.Lazy (eitherResult, parse) -- base import Data.Fixed (HasResolution(..), Fixed) import Data.Functor ((<$>)) +import Data.Monoid (Monoid(mempty)) import Numeric (readFloat) -- bytestring @@ -114,3 +115,11 @@ parseCryptsyNum (unpack -> str) = instance FromJSON CryptsyNum where -- |Only accepts Text values parseJSON = withText "CrypstyNum" parseCryptsyNum + +-- |Like 'Data.Aeson.withArray' but also accepting JSON nulls as mempty. +withNullableArray :: (Monoid a) + => String -> (Array -> Parser a) -> Value -> Parser a +withNullableArray _ parseArray (Array v) = parseArray v +withNullableArray _ _ Null = return mempty +withNullableArray name _ v = typeMismatch name v + diff --git a/Cryptsy/API/Public/Market.hs b/Cryptsy/API/Public/Market.hs index 6bb9f47..88e322c 100644 --- a/Cryptsy/API/Public/Market.hs +++ b/Cryptsy/API/Public/Market.hs @@ -1,13 +1,14 @@ module Cryptsy.API.Public.Market ( GMarket(..), Market , withComponents, withText - , module Cryptsy.API.Public , module Cryptsy.API.Public.Order , module Cryptsy.API.Public.Trade + -- |Re-exported from Cryptsy.API.Public + , CryptsyNum(..), E8, Fixed -- |Re-exported from Data.Text , Text -- |Re-exported from Data.Aeson - , Value, Object, FromJSON(..) + , FromJSON(..), Value, Object -- |Re-exported from Data.Aeson.Types , Parser -- |Re-exported from Data.Vector @@ -17,10 +18,8 @@ where -- base import Control.Applicative ((<$>), (<*>)) -import Control.Monad (return, (>>=)) +import Control.Monad ((>>=)) import Data.Function ((.), ($)) -import Data.Monoid (Monoid(mempty)) -import Data.String (String) import Data.Traversable (mapM) import Prelude () import Text.Show (Show) @@ -31,18 +30,18 @@ import Data.Text (Text, pack) -- aeson import qualified Data.Aeson as Aeson import Data.Aeson - ( FromJSON(..), Value(Array, Null), Array, Object, withObject, (.:) + ( FromJSON(..), Value, Object, withObject, (.:) ) -import Data.Aeson.Types (Parser, typeMismatch) +import Data.Aeson.Types (Parser) -- vector import Data.Vector (Vector) -- this package -import Cryptsy.API.Public (CryptsyNum(..)) -import Cryptsy.API.Public.Order (GOrder(Order)) +import Cryptsy.API.Public (CryptsyNum(..), E8, Fixed, withNullableArray) +import Cryptsy.API.Public.Order (GOrder(Order), Order) import qualified Cryptsy.API.Public.Order as Order -import Cryptsy.API.Public.Trade (GTrade(Trade, time)) +import Cryptsy.API.Public.Trade (GTrade(Trade, time), Trade) import qualified Cryptsy.API.Public.Trade as Trade data GMarket p q dt t = Market @@ -67,12 +66,6 @@ instance (FromJSON p, FromJSON q, FromJSON dt, FromJSON t) => parseJSON = withObject "market" $ withComponents parseJSON parseJSON parseJSON parseJSON -withNullableArray :: (Monoid a) - => String -> (Array -> Parser a) -> Value -> Parser a -withNullableArray _ parseArray (Array v) = parseArray v -withNullableArray _ _ Null = return mempty -withNullableArray name _ v = typeMismatch name v - -- Combine component parsers into JSON Object parser, for use with -- 'Aeson.withObject'. withComponents :: (Value -> Parser p) -- ^ price parser diff --git a/Cryptsy/API/Public/OrderBook.hs b/Cryptsy/API/Public/OrderBook.hs new file mode 100644 index 0000000..ffba3e7 --- /dev/null +++ b/Cryptsy/API/Public/OrderBook.hs @@ -0,0 +1,81 @@ +module Cryptsy.API.Public.OrderBook + ( GOrderBook(..), OrderBook + , withComponents, withOrder + , module Cryptsy.API.Public.Order + -- |Re-export from Cyptsy.API.Public + , CryptsyNum(..) + -- |Re-exported from Data.Aeson + , FromJSON(..), Value, Object + -- |Re-export from Data.Text + , Text + -- |Re-export from Data.Vector + , Vector + ) +where + +-- aeson +import Data.Aeson (FromJSON(..), Value, Object, withObject, (.:)) +import Data.Aeson.Types (Parser) + +-- base +import Control.Applicative ((<*>)) +import Control.Monad ((>>=)) +import Data.Function (($)) +import Data.Functor ((<$>)) +import Data.Traversable (mapM) +import Prelude () +import Text.Show (Show) + +-- text +import Data.Text (Text, pack) + +-- vector +import Data.Vector (Vector) + +-- this package +import Cryptsy.API.Public (CryptsyNum(..), withNullableArray) +import Cryptsy.API.Public.Order (GOrder(Order), Order) + +-- |general order book +data GOrderBook p q t = OrderBook + { marketid :: Text + , label :: Text + , primaryname :: Text + , primarycode :: Text + , secondaryname :: Text + , secondarycode :: Text + , sellorders :: Vector (GOrder p q t) + , buyorders :: Vector (GOrder p q t) + } deriving Show + +-- |default order book +type OrderBook = GOrderBook CryptsyNum CryptsyNum CryptsyNum + +instance (FromJSON p, FromJSON q, FromJSON t) => + FromJSON (GOrderBook p q t) + where + parseJSON = withObject "OrderBook" $ withComponents parseJSON + +-- |Build parser for orderbook from parser for a single order. +withComponents :: (Value -> Parser (GOrder p q t)) -- ^ order parser + -> Object -> Parser (GOrderBook p q t) +withComponents parseOrder o = + OrderBook <$> + o .: pack "marketid" <*> + o .: pack "label" <*> + o .: pack "primaryname" <*> + o .: pack "primarycode" <*> + o .: pack "secondaryname" <*> + o .: pack "secondarycode" <*> + (o .: pack "sellorders" >>= parseSellOrders) <*> + (o .: pack "buyorders" >>= parseBuyOrders) + where + parseOrders = mapM parseOrder + parseSellOrders = withNullableArray "sellorders" $ parseOrders + parseBuyOrders = withNullableArray "buyorders" $ parseOrders + +-- |Build parser for orderbook from parser for a single order object. +withOrder :: (Object -> Parser (GOrder p q t)) -- ^ order parser + -> Object -> Parser (GOrderBook p q t) +withOrder parseOrder = withComponents (withObject "Order" parseOrder) + diff --git a/Cryptsy/API/Public/OrderData.hs b/Cryptsy/API/Public/OrderData.hs new file mode 100644 index 0000000..7f77d2e --- /dev/null +++ b/Cryptsy/API/Public/OrderData.hs @@ -0,0 +1,54 @@ +module Cryptsy.API.Public.OrderData + ( GOrderData(..), OrderData + , withComponents, withOrderBook + , module Cryptsy.API.Public.OrderBook + -- |Re-export from Cryptsy.API.Public + , CryptsyNum(..), Parser, Text, Value + -- |Re-export from Data.Aeson + , FromJSON(..), Object + -- |Re-exported from Data.HashMap.Strict + , HashMap + ) +where + +-- aeson +import Data.Aeson (FromJSON(..), Object, withObject) + +-- base +import Data.Function (($)) +import Data.Functor (fmap, (<$>)) +import Data.Traversable (mapM) +import Prelude () +import Text.Show (Show) + +-- unordered-containers +import Data.HashMap.Strict (HashMap) + +-- this package +import Cryptsy.API.Public +import Cryptsy.API.Public.OrderBook (GOrderBook(..), OrderBook) + +-- |general order data parameterized by types for prices, quantities, +-- and totals (price * quantity) +newtype GOrderData p q t = + OrderData { orderbooks :: HashMap Text (GOrderBook p q t) } + deriving Show + +-- |default order data +type OrderData = GOrderData CryptsyNum CryptsyNum CryptsyNum + +instance (FromJSON p, FromJSON q, FromJSON t) => + FromJSON (GOrderData p q t) + where + parseJSON = withObject "OrderData" $ withComponents parseJSON + +-- |Build parser for multiple markets from parser for single market. +withComponents :: (Value -> Parser (GOrderBook p q t)) -- ^ orderbook parser + -> Object -> Parser (GOrderData p q t) +withComponents parseBook = fmap OrderData <$> mapM parseBook + +-- |Build parser for multiple markets from parser for single market object. +withOrderBook :: (Object -> Parser (GOrderBook p q t)) -- ^ orderbook parser + -> Object -> Parser (GOrderData p q t) +withOrderBook parseBook = withComponents (withObject "OrderBook" parseBook) + diff --git a/Main.hs b/Main.hs index e44ba27..9be818c 100644 --- a/Main.hs +++ b/Main.hs @@ -69,6 +69,7 @@ import qualified Data.HashMap.Strict as HM (lookup) -- this package import Cryptsy.API.Public.MarketData.New import qualified Cryptsy.API.Public.MarketData.Old as Old () +import qualified Cryptsy.API.Public.OrderData as OrderData () import qualified Cryptsy.API.Public.Trade as Trade ( price, quantity, total ) -- 2.11.4.GIT