1 {-# LANGUAGE ViewPatterns, NoMonomorphismRestriction #-}
8 import Control
.Applicative
((<*>))
9 import Control
.Monad
(Monad
, return, (<=<))
10 import Data
.Bool (Bool(False, True))
11 import Data
.Either (either)
12 import Data
.Foldable
(foldMap
)
13 import Data
.Function
(const, id, on
)
14 import Data
.Functor
(fmap, (<$>))
15 import Data
.List
(minimum, maximum)
16 import Data
.Maybe (Maybe(Nothing
, Just
), catMaybes, mapMaybe)
17 import Data
.Monoid
(Monoid
(..), Sum
(..), (<>))
18 import Data
.Ord
(Ord
(..), min, max)
19 import Data
.Tuple
(curry, uncurry)
21 ( Num
, Show(show), Fractional
(fromRational)
23 , print, putStr, putStrLn, read, take, undefined
29 import qualified Data
.ByteString
as BS
30 import qualified Data
.ByteString
.Char8
as CS
33 import Network
.URI
(URI
, parseAbsoluteURI
)
36 import Codec
.Utils
(Octet
)
37 import Data
.HMAC
(HashMethod
(..), hmac
)
38 import qualified Data
.Digest
.SHA512
as SHA512
(hash
)
41 import Network
.HTTP
.Base
42 ( Request
(..), Response
(..), RequestMethod
(..)
43 , urlEncodeVars
, httpPackageVersion
46 import Network
.HTTP
.Headers
(Header
(Header
), HeaderName
(HdrCustom
))
48 import qualified Network
.Browser
as Browser
(request
)
49 import Network
.Browser
50 ( Proxy
(NoProxy
), BrowserAction
51 , browse
, setAllowRedirects
, setMaxRedirects
, setAuthorities
52 , setAllowBasicAuth
, setMaxErrorRetries
, setMaxPoolSize
53 , setMaxAuthAttempts
, setProxy
, setOutHandler
, setUserAgent
57 import Control
.Monad
.Trans
.Either
58 ( EitherT
(..), bimapEitherT
, eitherT
, runEitherT
, hoistEither
62 import Control
.Error
.Util
(note
)
65 import qualified Data
.Text
as T
68 import Control
.Monad
.IO.Class
(liftIO
)
69 import Control
.Monad
.Trans
.Class
(lift
)
71 -- unordered-containers
72 import qualified Data
.HashMap
.Strict
as HM
(lookup, size
)
75 import qualified Data
.Vector
as V
(length)
78 import Cryptsy
.API
.Public
.MarketData
.New
79 import qualified Cryptsy
.API
.Public
.MarketData
.Old
as Old
80 import Cryptsy
.API
.Public
.Market
(singleMarket
)
81 import qualified Cryptsy
.API
.Public
.Market
as Market
82 import Cryptsy
.API
.Public
.OrderData
83 import Cryptsy
.API
.Public
.OrderBook
(singleOrderBook
)
84 import qualified Cryptsy
.API
.Public
.OrderBook
as OrderBook
85 import qualified Cryptsy
.API
.Public
.Trade
as Trade
86 ( price
, quantity
, total
)
89 apiUrl
= "https://www.cryptsy.com/api"
92 mUri
= parseAbsoluteURI apiUrl
95 apiPubkey
= "5a8808b25e3f59d8818d3fbc0ce993fbb82dcf90"
100 get_nonce
:: IO Integer
107 sha512_hm
:: HashMethod
108 sha512_hm
= HashMethod
109 { digest
= SHA512
.hash
110 , input_blocksize
= 1024
113 hexdump
:: [Octet
] -> String
114 hexdump
= mconcat
. fmap show2Hex
118 readHex :: String -> Maybe [Octet
]
122 oldMain
= case (readHex apiPrivkey
, mUri
) of
125 putStrLn " could not be parsed as an absolute URI."
127 putStrLn "<apiPrivkey> could not be parsed as hex."
128 (Just pk
, Just uri
) -> do
131 postString
= urlEncodeVars
[ ("nonce", show nonce
) ]
132 postSig
= hexdump
. hmac sha512_hm pk
. BS
.unpack
$ CS
.pack postString
137 [ Header
(HdrCustom
"Key" ) apiPubkey
138 , Header
(HdrCustom
"Sign") postSig
140 , rqBody
= postString
142 myBody
<- browse
$ do
144 (_
, theResponse
) <- Browser
.request request
145 return $ rspBody theResponse
148 initBrowser
:: BrowserAction t
()
150 setAllowRedirects
True
151 setMaxRedirects
$ Just
10
153 setAllowBasicAuth
False
154 setMaxErrorRetries
$ Just
0
155 setMaxPoolSize
$ Just
2
156 setMaxAuthAttempts
$ Just
0
158 setOutHandler
. const $ return ()
159 setUserAgent
$ "Network.Browser/" <> httpPackageVersion
161 newtype Min n
= Min
{ getMin
:: Maybe n
} deriving Show
162 instance Ord n
=> Monoid
(Min n
) where
164 mappend
(Min Nothing
) m
= m
165 mappend m
(Min Nothing
) = m
166 mappend
(Min
(Just m
)) (Min
(Just n
)) = Min
. Just
$ min m n
167 mconcat
[] = Min Nothing
168 mconcat mins
= Min
. Just
. minimum . catMaybes $ fmap getMin mins
170 newtype Max n
= Max
{ getMax
:: Maybe n
} deriving Show
171 instance Ord n
=> Monoid
(Max n
) where
173 mappend
(Max Nothing
) m
= m
174 mappend m
(Max Nothing
) = m
175 mappend
(Max
(Just m
)) (Max
(Just n
)) = Max
. Just
$ max m n
176 mconcat
[] = Max Nothing
177 mconcat maxs
= Max
. Just
. maximum $ mapMaybe getMax maxs
179 data MarketSummary p q t
= MarketSummary
182 , count
:: Sum
Integer
183 , primaryVolume
:: Sum q
184 , secondaryVolume
:: Sum t
186 instance (Ord p
, Num q
, Num t
) => Monoid
(MarketSummary p q t
) where
187 mempty
= MarketSummary
193 mappend
= curry $ MarketSummary
<$>
194 uncurry (mappend `on` minPrice
) <*>
195 uncurry (mappend `on` maxPrice
) <*>
196 uncurry (mappend `on` count
) <*>
197 uncurry (mappend `on` primaryVolume
) <*>
198 uncurry (mappend `on` secondaryVolume
)
199 mconcat
= MarketSummary
<$>
200 (mconcat
. fmap minPrice
) <*>
201 (mconcat
. fmap maxPrice
) <*>
202 (mconcat
. fmap count
) <*>
203 (mconcat
. fmap primaryVolume
) <*>
204 (mconcat
. fmap secondaryVolume
)
206 tradeSummary
:: GTrade dt p q t
-> MarketSummary p q t
207 tradeSummary trade
= MarketSummary
208 { minPrice
= Min
. Just
$ Trade
.price trade
209 , maxPrice
= Max
. Just
$ Trade
.price trade
211 , primaryVolume
= Sum
$ Trade
.quantity trade
212 , secondaryVolume
= Sum
$ Trade
.total trade
216 main
= either print return <=< browse
. runEitherT
$ do
218 books
<- jsonOrderData
219 liftIO
. print . HM
.size
$ orderbooks
(books
:: OrderData
)
220 omkts
<- Old
.jsonMarketData
221 liftIO
. print . HM
.size
$ Old
.markets
(omkts
:: MarketData
)
222 mkts
<- jsonMarketData
223 liftIO
. print . HM
.size
$ markets
(mkts
:: MarketData
)
224 book
<- singleOrderBook
$ T
.pack
"113"
225 liftIO
. print $ ( V
.length $ OrderBook
.buyorders
(book
:: OrderBook
)
226 , V
.length $ OrderBook
.sellorders book
228 market
<- singleMarket
$ T
.pack
"113"
229 liftIO
. print $ ( V
.length $ recenttrades
(market
:: Market
)
230 , V
.length $ Market
.buyorders market
231 , V
.length $ Market
.sellorders market