1 {-# LANGUAGE ViewPatterns, NoMonomorphismRestriction #-}
8 import Control
.Applicative
((<*>))
9 import Control
.Monad
(Monad
, return)
10 import Data
.Bool (Bool(False, True))
11 import Data
.Foldable
(foldMap
)
12 import Data
.Function
(const, id, on
)
13 import Data
.Functor
(fmap, (<$>))
14 import Data
.List
(minimum, maximum)
15 import Data
.Maybe (Maybe(Nothing
, Just
), catMaybes, mapMaybe)
16 import Data
.Monoid
(Monoid
(..), Sum
(..), (<>))
17 import Data
.Ord
(Ord
(..), min, max)
18 import Data
.Tuple
(curry, uncurry)
20 ( Num
, Show(show), Fractional
(fromRational)
22 , print, putStr, putStrLn, read, take, undefined
28 import qualified Data
.ByteString
as BS
29 import qualified Data
.ByteString
.Char8
as CS
32 import Network
.URI
(URI
, parseAbsoluteURI
)
35 import Codec
.Utils
(Octet
)
36 import Data
.HMAC
(HashMethod
(..), hmac
)
37 import qualified Data
.Digest
.SHA512
as SHA512
(hash
)
40 import Network
.HTTP
.Base
41 ( Request
(..), Response
(..), RequestMethod
(..)
42 , urlEncodeVars
, httpPackageVersion
45 import Network
.HTTP
.Headers
(Header
(Header
), HeaderName
(HdrCustom
))
47 import qualified Network
.Browser
as Browser
(request
)
48 import Network
.Browser
49 ( Proxy
(NoProxy
), BrowserAction
50 , browse
, setAllowRedirects
, setMaxRedirects
, setAuthorities
51 , setAllowBasicAuth
, setMaxErrorRetries
, setMaxPoolSize
52 , setMaxAuthAttempts
, setProxy
, setOutHandler
, setUserAgent
56 import Control
.Monad
.Trans
.Either
57 ( EitherT
(..), bimapEitherT
, eitherT
, runEitherT
, hoistEither
61 import Control
.Error
.Util
(note
)
64 import qualified Data
.Text
as T
66 -- unordered-containers
67 import qualified Data
.HashMap
.Strict
as HM
(lookup)
70 import Cryptsy
.API
.Public
.MarketData
.New
71 import qualified Cryptsy
.API
.Public
.MarketData
.Old
as Old
()
72 import Cryptsy
.API
.Public
.OrderData
73 import qualified Cryptsy
.API
.Public
.Trade
as Trade
74 ( price
, quantity
, total
)
77 apiUrl
= "https://www.cryptsy.com/api"
80 mUri
= parseAbsoluteURI apiUrl
83 apiPubkey
= "5a8808b25e3f59d8818d3fbc0ce993fbb82dcf90"
88 get_nonce
:: IO Integer
95 sha512_hm
:: HashMethod
96 sha512_hm
= HashMethod
97 { digest
= SHA512
.hash
98 , input_blocksize
= 1024
101 hexdump
:: [Octet
] -> String
102 hexdump
= mconcat
. fmap show2Hex
106 readHex :: String -> Maybe [Octet
]
110 oldMain
= case (readHex apiPrivkey
, mUri
) of
113 putStrLn " could not be parsed as an absolute URI."
115 putStrLn "<apiPrivkey> could not be parsed as hex."
116 (Just pk
, Just uri
) -> do
119 postString
= urlEncodeVars
[ ("nonce", show nonce
) ]
120 postSig
= hexdump
. hmac sha512_hm pk
. BS
.unpack
$ CS
.pack postString
125 [ Header
(HdrCustom
"Key" ) apiPubkey
126 , Header
(HdrCustom
"Sign") postSig
128 , rqBody
= postString
130 myBody
<- browse
$ do
132 (_
, theResponse
) <- Browser
.request request
133 return $ rspBody theResponse
136 initBrowser
:: BrowserAction t
()
138 setAllowRedirects
True
139 setMaxRedirects
$ Just
10
141 setAllowBasicAuth
False
142 setMaxErrorRetries
$ Just
0
143 setMaxPoolSize
$ Just
2
144 setMaxAuthAttempts
$ Just
0
146 setOutHandler
. const $ return ()
147 setUserAgent
$ "Network.Browser/" <> httpPackageVersion
149 newtype Min n
= Min
{ getMin
:: Maybe n
} deriving Show
150 instance Ord n
=> Monoid
(Min n
) where
152 mappend
(Min Nothing
) m
= m
153 mappend m
(Min Nothing
) = m
154 mappend
(Min
(Just m
)) (Min
(Just n
)) = Min
. Just
$ min m n
155 mconcat
[] = Min Nothing
156 mconcat mins
= Min
. Just
. minimum . catMaybes $ fmap getMin mins
158 newtype Max n
= Max
{ getMax
:: Maybe n
} deriving Show
159 instance Ord n
=> Monoid
(Max n
) where
161 mappend
(Max Nothing
) m
= m
162 mappend m
(Max Nothing
) = m
163 mappend
(Max
(Just m
)) (Max
(Just n
)) = Max
. Just
$ max m n
164 mconcat
[] = Max Nothing
165 mconcat maxs
= Max
. Just
. maximum $ mapMaybe getMax maxs
167 data MarketSummary p q t
= MarketSummary
170 , count
:: Sum
Integer
171 , primaryVolume
:: Sum q
172 , secondaryVolume
:: Sum t
174 instance (Ord p
, Num q
, Num t
) => Monoid
(MarketSummary p q t
) where
175 mempty
= MarketSummary
181 mappend
= curry $ MarketSummary
<$>
182 uncurry (mappend `on` minPrice
) <*>
183 uncurry (mappend `on` maxPrice
) <*>
184 uncurry (mappend `on` count
) <*>
185 uncurry (mappend `on` primaryVolume
) <*>
186 uncurry (mappend `on` secondaryVolume
)
187 mconcat
= MarketSummary
<$>
188 (mconcat
. fmap minPrice
) <*>
189 (mconcat
. fmap maxPrice
) <*>
190 (mconcat
. fmap count
) <*>
191 (mconcat
. fmap primaryVolume
) <*>
192 (mconcat
. fmap secondaryVolume
)
194 tradeSummary
:: GTrade dt p q t
-> MarketSummary p q t
195 tradeSummary trade
= MarketSummary
196 { minPrice
= Min
. Just
$ Trade
.price trade
197 , maxPrice
= Max
. Just
$ Trade
.price trade
199 , primaryVolume
= Sum
$ Trade
.quantity trade
200 , secondaryVolume
= Sum
$ Trade
.total trade
204 main
= eitherT
putStrLn print $ do
205 MarketData mkts
<- bimapEitherT
show id . EitherT
. browse
$ do
207 _
<- runEitherT
(jsonOrderData
:: PubCryptsy OrderData
)
208 runEitherT
(jsonMarketData
:: PubCryptsy MarketData
)
209 mkt
<- hoistEither
. note
"No 'LTC/BTC' market."
210 $ HM
.lookup (T
.pack
"LTC/BTC") mkts
211 return . foldMap tradeSummary
$ recenttrades mkt