haskell-bitmex-client (empty) → 0.1.0.0
raw patch · 17 files changed
+1761/−0 lines, 17 filesdep +aesondep +basedep +bytestringsetup-changed
Dependencies added: aeson, base, bytestring, bytestring-conversion, cryptonite, haskell-bitmex-client, haskell-bitmex-rest, http-client, http-client-tls, http-types, katip, memory, microlens, mtl, network, safe-exceptions, text, time, vector, websockets, wuss
Files
- ChangeLog.md +5/−0
- LICENSE +30/−0
- README.md +9/−0
- Setup.hs +2/−0
- example/Example.hs +168/−0
- haskell-bitmex-client.cabal +80/−0
- lib/BitMEXClient.hs +6/−0
- lib/BitMEXClient/CustomPrelude.hs +93/−0
- lib/BitMEXClient/WebSockets.hs +5/−0
- lib/BitMEXClient/WebSockets/Types.hs +7/−0
- lib/BitMEXClient/WebSockets/Types/General.hs +124/−0
- lib/BitMEXClient/WebSockets/Types/Request.hs +107/−0
- lib/BitMEXClient/WebSockets/Types/Response.hs +755/−0
- lib/BitMEXClient/Wrapper.hs +7/−0
- lib/BitMEXClient/Wrapper/API.hs +266/−0
- lib/BitMEXClient/Wrapper/Logging.hs +47/−0
- lib/BitMEXClient/Wrapper/Types.hs +50/−0
+ ChangeLog.md view
@@ -0,0 +1,5 @@+# Revision history for haskell-bitmex-client++## 0.1.0.0 -- YYYY-mm-dd++* First version. Released on an unsuspecting world.
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2018, Lucsanszky++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Lucsanszky nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,9 @@+# haskell-bitmex-client++Haskell API for the BitMEX cryptocurrency exchange. Contains the WebSocket API and a wrapper+around the auto-generated REST client (http://hackage.haskell.org/package/haskell-bitmex-rest).+The API is almost complete (TradeBins endpoint for the WS API is missing, no support for multiplexing) but it is rough around the edges. Suggestions for improvement and contributions are welcome!++## Building+Only Cabal is supported for now. Just run `cabal new-build` to build the library.+To run the example, just run `cabal new-run example [path/to/api/publickey] [path/to/api/privatekey]`. A Nix build will be added soon and feel free to add support for Stack.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ example/Example.hs view
@@ -0,0 +1,168 @@+{-# LANGUAGE OverloadedStrings #-}++module Main where++import qualified BitMEX as Mex+ ( Accept (..)+ , ContentType (..)+ , Leverage (..)+ , MimeJSON (..)+ , MimeResult+ , Position+ , Symbol (..)+ , initLogContext+ , orderGetOrders+ , positionUpdateLeverage+ , runDefaultLogExecWithContext+ , stdoutLoggingContext+ , _setBodyLBS+ )+import BitMEXClient+ ( BitMEXApp+ , BitMEXReader+ , BitMEXWrapperConfig (..)+ , Command (..)+ , Environment (..)+ , Symbol (..)+ , Topic (..)+ , connect+ , getMessage+ , makeRequest+ , makeTimestamp+ , sendMessage+ , sign+ , withConnectAndSubscribe+ )+import Control.Concurrent (forkIO)+import Control.Exception+import Control.Monad (forever, unless)+import Control.Monad.Reader (liftIO)+import qualified Control.Monad.Reader as R (ask, asks)+import Data.Aeson+ ( Value (String)+ , decode+ , toJSON+ )+import Data.Aeson+import Data.ByteString (readFile)+import Data.ByteString.Char8 (pack)+import Data.Monoid+import Data.Text (Text, null)+import qualified Data.Text as T (pack)+import qualified Data.Text.IO as T+ ( getLine+ , readFile+ )+import Data.Time.Clock.POSIX (getPOSIXTime)+import Katip+import Network.HTTP.Client (newManager)+import Network.HTTP.Client.TLS+ ( tlsManagerSettings+ )+import Network.WebSockets+ ( receiveData+ , sendClose+ , sendTextData+ )+import Prelude+ ( Bool (True)+ , IO+ , Maybe (..)+ , mempty+ , print+ , return+ , show+ , ($)+ , (++)+ , (.)+ , (<$>)+ , (=<<)+ , (>>)+ , (>>=)+ )+import System.Environment (getArgs)+import System.IO (stdout)++updateLeverage ::+ Symbol+ -> Mex.Leverage+ -> BitMEXReader (Mex.MimeResult Mex.Position)+updateLeverage sym lev = do+ let leverageTemplate =+ Mex.positionUpdateLeverage+ (Mex.ContentType Mex.MimeJSON)+ (Mex.Accept Mex.MimeJSON)+ (Mex.Symbol ((T.pack . show) sym))+ lev+ leverageRequest =+ Mex._setBodyLBS leverageTemplate $+ "{\"leverage\": " <> encode (Mex.unLeverage lev) <>+ ", \"symbol\": " <>+ encode ((T.pack . show) sym) <>+ "}"+ makeRequest leverageRequest++app :: BitMEXApp ()+app conn = do+ config <- R.ask+ pub <- R.asks publicKey+ time <- liftIO $ makeTimestamp <$> getPOSIXTime+ sig <- sign (pack ("GET" <> "/realtime" <> show time))+ -- Example usage of makeRequest+ x <-+ makeRequest $+ Mex.orderGetOrders (Mex.Accept Mex.MimeJSON)+ _ <- updateLeverage XBTUSD (Mex.Leverage 3)+ liftIO $ do+ print x+ _ <-+ forkIO $+ sendMessage+ conn+ AuthKey+ [ String pub+ , toJSON time+ , (toJSON . show) sig+ ]+ _ <-+ forkIO $+ forever $ do getMessage conn config >>= print+ _ <-+ forkIO $+ sendMessage+ conn+ Subscribe+ [OrderBook10 XBTUSD :: Topic Symbol]+ loop+ sendClose conn ("Connection closed" :: Text)+ where+ loop =+ T.getLine >>= \line ->+ unless (null line) $+ sendTextData conn line >> loop++main :: IO ()+main = do+ mgr <- newManager tlsManagerSettings+ (pubPath:privPath:_) <- getArgs+ pub <- T.readFile pubPath+ priv <- readFile privPath+ logCxt <- Mex.initLogContext+ let config =+ BitMEXWrapperConfig+ { environment = TestNet+ , pathREST = Just "/api/v1"+ , pathWS = Just "/realtime"+ , manager = Just mgr+ , publicKey = pub+ , privateKey = priv+ , logExecContext =+ Mex.runDefaultLogExecWithContext+ , logContext = logCxt+ }+ -- Example usage of withConnectAndSubscribe+ withConnectAndSubscribe config [Margin] $ \c -> do+ getMessage c config >>= print+ sendClose c ("Connection closed" :: Text)+ -- Example usage of connect+ connect config app
+ haskell-bitmex-client.cabal view
@@ -0,0 +1,80 @@+name: haskell-bitmex-client+version: 0.1.0.0+synopsis: Complete BitMEX Client+description: A complete BitMEX client library including the WebSocket API and a wrapper around the auto-generated REST API (haskell-bitmex-rest).+license: BSD3+license-file: LICENSE+author: Lucsanszky+maintainer: dan.lucsanszky@gmail.com+category: Web+build-type: Simple+extra-source-files: ChangeLog.md+ README.md+cabal-version: >=1.10++library+ exposed-modules: BitMEXClient+ , BitMEXClient.WebSockets+ , BitMEXClient.Wrapper+ , BitMEXClient.WebSockets.Types+ , BitMEXClient.WebSockets.Types.General+ , BitMEXClient.WebSockets.Types.Request+ , BitMEXClient.WebSockets.Types.Response+ , BitMEXClient.Wrapper.API+ , BitMEXClient.Wrapper.Logging+ , BitMEXClient.Wrapper.Types+ other-modules: BitMEXClient.CustomPrelude+ ghc-options: -Wall+ -funbox-strict-fields+ -fwarn-unused-imports+ -Wcompat+ -Wincomplete-record-updates+ -Wincomplete-uni-patterns+ -Wredundant-constraints+ build-depends: base >=4.10 && <4.11+ , mtl >=2.2.1+ , aeson >=1.0 && <2.0+ , bytestring >=0.10.0 && <0.11+ , bytestring-conversion >= 0.3+ , cryptonite >= 0.25+ , http-client >=0.5 && <0.6+ , http-client-tls >= 0.3+ , http-types >= 0.10+ , katip >= 0.5+ , microlens >= 0.4+ , memory >= 0.14+ , text >=0.11 && <1.3+ , time >=1.5 && <1.9+ , haskell-bitmex-rest+ , safe-exceptions <0.2+ , network >= 2.6+ , vector >=0.10.9 && <0.13+ , websockets >= 0.12+ , wuss >= 1.1+ hs-source-dirs: lib+ default-language: Haskell2010+ default-extensions: DeriveGeneric+ DuplicateRecordFields+ GeneralizedNewtypeDeriving+ MultiParamTypeClasses+ NoImplicitPrelude+ OverloadedStrings+ RankNTypes+ RecordWildCards++executable example+ hs-source-dirs: example+ main-is: Example.hs+ build-depends: base >=4.10 && <4.11+ , mtl >=2.2.1+ , aeson >=1.0 && <2.0+ , bytestring >=0.10.0 && <0.11+ , haskell-bitmex-rest+ , haskell-bitmex-client+ , http-client >=0.5 && <0.6+ , http-client-tls >= 0.3+ , katip >= 0.5+ , text >=0.11 && <1.3+ , time >=1.5 && <1.9+ , websockets >= 0.12+ default-language: Haskell2010
+ lib/BitMEXClient.hs view
@@ -0,0 +1,6 @@+module BitMEXClient+ ( module X+ ) where++import BitMEXClient.WebSockets as X+import BitMEXClient.Wrapper as X
+ lib/BitMEXClient/CustomPrelude.hs view
@@ -0,0 +1,93 @@+module BitMEXClient.CustomPrelude+ ( module X+ ) where++import Control.Exception.Safe as X (MonadCatch)+import Control.Monad as X (fail)+import Control.Monad.Reader as X+ ( Monad+ , MonadIO+ , MonadReader+ , MonadTrans+ , ReaderT+ , asks+ , liftIO+ , runReaderT+ )+import Crypto.Hash as X (Digest)+import Crypto.Hash.Algorithms as X (SHA256)+import Crypto.MAC.HMAC as X+ ( hmac+ , hmacGetDigest+ )+import Data.Aeson as X+ ( FromJSON+ , SumEncoding (UntaggedValue)+ , ToJSON+ , Value (..)+ , constructorTagModifier+ , decode+ , defaultOptions+ , encode+ , fieldLabelModifier+ , genericParseJSON+ , genericToJSON+ , parseJSON+ , sumEncoding+ , toJSON+ , withObject+ , (.:?)+ )+import Data.Char as X (toLower)+import Data.Monoid as X ((<>))+import Data.Time.Clock.POSIX as X (getPOSIXTime)+import GHC.Generics as X+import Lens.Micro as X ((.~), (^.))+import Network.HTTP.Client as X+ ( Manager+ , newManager+ )+import Network.HTTP.Client.TLS as X+ ( tlsManagerSettings+ )+import Network.HTTP.Types.URI as X (renderQuery)+import Network.Socket as X+ ( withSocketsDo+ )+import Network.WebSockets as X+ ( ClientApp+ , Connection+ , receiveData+ , sendTextData+ )+import Prelude as X+ ( Applicative+ , Bool (..)+ , Double+ , Eq+ , Functor+ , IO+ , Int+ , Integer+ , Maybe (..)+ , RealFrac+ , Show+ , String+ , drop+ , filter+ , floor+ , head+ , map+ , return+ , show+ , ($)+ , (*)+ , (++)+ , (.)+ , (/=)+ , (<$>)+ , (>>=)+ )+import Wuss as X+ ( runSecureClient+ )
+ lib/BitMEXClient/WebSockets.hs view
@@ -0,0 +1,5 @@+module BitMEXClient.WebSockets+ ( module X+ ) where++import BitMEXClient.WebSockets.Types as X
+ lib/BitMEXClient/WebSockets/Types.hs view
@@ -0,0 +1,7 @@+module BitMEXClient.WebSockets.Types+ ( module X+ ) where++import BitMEXClient.WebSockets.Types.General as X+import BitMEXClient.WebSockets.Types.Request as X+import BitMEXClient.WebSockets.Types.Response as X
+ lib/BitMEXClient/WebSockets/Types/General.hs view
@@ -0,0 +1,124 @@+module BitMEXClient.WebSockets.Types.General+ ( Symbol(..)+ , Currency(..)+ , Side(..)+ , OrderType(..)+ , ExecutionInstruction(..)+ , ContingencyType(..)+ ) where++import BitMEXClient.CustomPrelude++data Side+ = Buy+ | Sell+ deriving (Eq, Show, Generic)++instance FromJSON Side++instance ToJSON Side++data Currency+ = XBT+ | XBt+ | USD+ | A50+ | ADA+ | BCH+ | BFX+ | BLOCKS+ | BVOL+ | COIN+ | DAO+ | DASH+ | EOS+ | ETC+ | ETH+ | FCT+ | GNO+ | LSK+ | LTC+ | NEO+ | QTUM+ | REP+ | SEGWIT+ | SNT+ | WIN+ | XBC+ | XBJ+ | XBU+ | XLM+ | XLT+ | XMR+ | XRP+ | XTZ+ | ZEC+ | Total+ deriving (Eq, Show, Generic)++instance ToJSON Currency++instance FromJSON Currency++data Symbol+ = XBTUSD+ | XBTM18+ | XBTU18+ | XBT7D_U110+ | ADAM18+ | BCHM18+ | ETHM18+ | LTCM18+ | XRPM18+ deriving (Eq, Show, Generic)++instance ToJSON Symbol++instance FromJSON Symbol++data OrderType+ = Market+ | Limit+ | Stop+ | StopLimit+ | MarketIfTouched+ | LimitIfTouched+ | MarketWithLeftOverAsLimit+ | Pegged+ deriving (Eq, Show, Generic)++instance FromJSON OrderType++instance ToJSON OrderType++data ExecutionInstruction+ = ParticipateDoNotInitiate+ | AllOrNone+ | MarkPrice+ | IndexPrice+ | LastPrice+ | Close+ | ReduceOnly+ | Fixed+ deriving (Eq, Show, Generic)++instance FromJSON ExecutionInstruction++instance ToJSON ExecutionInstruction++data ContingencyType+ = OCO -- ^ One Cancels the Other+ | OTO -- ^ One Triggers the Other+ | OUOA -- ^ One Updates the Other Absoulute+ | OUOP -- ^ One Updates the Other Proportional+ deriving (Eq, Generic)++instance Show ContingencyType where+ show OCO = "OneCancelsTheOther"+ show OTO = "OneTriggersTheOther"+ show OUOA = "OneUpdatesTheOtherAbsolute"+ show OUOP = "OneUpdatesTheOtherProportional"++instance FromJSON ContingencyType++instance ToJSON ContingencyType
+ lib/BitMEXClient/WebSockets/Types/Request.hs view
@@ -0,0 +1,107 @@+module BitMEXClient.WebSockets.Types.Request+ ( Command(..)+ , Topic(..)+ , Message(..)+ ) where++import BitMEXClient.CustomPrelude+import BitMEXClient.WebSockets.Types.General+import qualified Data.Text as T+ ( append+ , pack+ )+import Data.Vector+ ( Vector+ )++data Command+ = Subscribe+ | Unsubscribe+ | Ping+ | CancelAllAfter+ | AuthKey+ deriving (Eq, Show, Generic)++instance ToJSON Command where+ toJSON = genericToJSON opts+ where+ opts =+ defaultOptions+ { constructorTagModifier =+ \xs -> case xs of+ [] -> xs+ (x : xs') -> (toLower x : xs')+ }++data Topic a+ = Announcement+ | Chat+ | Connected+ | Funding+ | Instrument+ | Insurance+ | Liquidation+ | OrderBookL2 Symbol+ | OrderBook10 Symbol+ | PublicNotifications+ | Quote Symbol+ | QuoteBin1m Symbol+ | QuoteBin5m Symbol+ | QuoteBin1h Symbol+ | QuoteBin1d Symbol+ | Settlement+ | Trade Symbol+ | TradeBin1m Symbol+ | TradeBin5m Symbol+ | TradeBin1h Symbol+ | TradeBin1d Symbol+ | Affiliate+ | Execution+ | Order+ | Margin+ | Position+ | PrivateNotifications+ | Transact+ | Wallet+ deriving (Eq, Show, Generic)++instance (ToJSON a) => ToJSON (Topic a) where+ toJSON (OrderBookL2 v) =+ String (T.append "orderBookL2:" ((T.pack . show) v))+ toJSON (OrderBook10 v) =+ String (T.append "orderBook10:" ((T.pack . show) v))+ toJSON (QuoteBin1m v) =+ String (T.append "quoteBin1m:" ((T.pack . show) v))+ toJSON (QuoteBin5m v) =+ String (T.append "quoteBin5m:" ((T.pack . show) v))+ toJSON (QuoteBin1h v) =+ String (T.append "quoteBin1h:" ((T.pack . show) v))+ toJSON (QuoteBin1d v) =+ String (T.append "quoteBin1d:" ((T.pack . show) v))+ toJSON (Trade v) =+ String (T.append "trade:" ((T.pack . show) v))+ toJSON (TradeBin1m v) =+ String (T.append "tradeBin1m:" ((T.pack . show) v))+ toJSON (TradeBin5m v) =+ String (T.append "tradeBin5m:" ((T.pack . show) v))+ toJSON (TradeBin1h v) =+ String (T.append "tradeBin1h:" ((T.pack . show) v))+ toJSON (TradeBin1d v) =+ String (T.append "tradeBin1d:" ((T.pack . show) v))+ toJSON v = genericToJSON opts v+ where+ opts =+ defaultOptions+ { constructorTagModifier =+ \xs -> case xs of+ [] -> xs+ (x : xs') -> (toLower x : xs')+ , sumEncoding = UntaggedValue+ }++data Message a = Message+ { op :: !Command+ , args :: !(Vector a)+ } deriving (Eq, Show, Generic)++instance ToJSON a => ToJSON (Message a)
+ lib/BitMEXClient/WebSockets/Types/Response.hs view
@@ -0,0 +1,755 @@+module BitMEXClient.WebSockets.Types.Response+ ( Response(..)+ , RespAffiliate(..)+ , RespAnnouncement(..)+ , RespChat(..)+ , RespConnectedUsers(..)+ , RespExecution(..)+ , RespFunding(..)+ , RespInstrument(..)+ , RespInsurance(..)+ , RespLiquidation(..)+ , RespMargin(..)+ , RespNotification(..)+ , RespOrder(..)+ , RespOrderBookL2(..)+ , RespOrderBook10(..)+ , RespPosition(..)+ , RespQuote(..)+ , RespSettlement(..)+ , RespTrade(..)+ , RespTransaction(..)+ , RespWallet(..)+ , TABLE(..)+ , STATUS(..)+ , INFO(..)+ , ERROR(..)+ , Action(..)+ ) where++import BitMEX.Core+ ( DateTime+ )+import BitMEXClient.CustomPrelude+import BitMEXClient.WebSockets.Types.General+import Data.Text+ ( Text+ )+import Data.Vector+ ( Vector+ )++data Action+ = Partial+ | Update+ | Insert+ | Delete+ deriving (Eq, Show, Generic)++instance FromJSON Action where+ parseJSON = genericParseJSON opts+ where+ opts =+ defaultOptions+ { constructorTagModifier =+ \xs ->+ case xs of+ [] -> xs+ (x:xs') -> (toLower x : xs')+ }++data NotificationType+ = NERROR+ | NINFO+ | NSUCCESS+ deriving (Eq, Show, Generic)++instance FromJSON NotificationType where+ parseJSON = genericParseJSON opts+ where+ opts =+ defaultOptions+ { constructorTagModifier =+ ((drop 1) . (map toLower))+ }++data TABLE a = TABLE+ { _table :: !Text+ , _action :: !Action+ , _data :: !(Vector a)+ , _keys :: !(Maybe (Vector Text))+ , _foreignKeys :: !(Maybe Value)+ , _types :: !(Maybe Value)+ , _filter :: !(Maybe Value)+ , _attributes :: !(Maybe Value)+ } deriving (Eq, Show, Generic)++instance (FromJSON a) => FromJSON (TABLE a)++data STATUS = STATUS+ { success :: !Bool+ , subscribe :: !(Maybe Text)+ , request :: !Value+ } deriving (Eq, Show, Generic)++instance FromJSON STATUS++data INFO = INFO+ { info :: !Text+ , version :: !Text+ , timestamp :: !Text+ , docs :: !Text+ , limit :: !Value+ } deriving (Eq, Show, Generic)++instance FromJSON INFO++data ERROR = ERROR+ { error :: !Text+ , status :: !(Maybe Int)+ , meta :: !(Maybe Value)+ , request :: !(Maybe Value)+ } deriving (Eq, Show, Generic)++instance FromJSON ERROR++data RespAffiliate = RespAffiliate+ { account :: !Double -- ^ /Required/ "account"+ , currency :: !Currency -- ^ /Required/ "currency"+ , prevPayout :: !(Maybe Double) -- ^ "prevPayout"+ , prevTurnover :: !(Maybe Double) -- ^ "prevTurnover"+ , prevComm :: !(Maybe Double) -- ^ "prevComm"+ , prevTimestamp :: !(Maybe DateTime) -- ^ "prevTimestamp"+ , execTurnover :: !(Maybe Double) -- ^ "execTurnover"+ , execComm :: !(Maybe Double) -- ^ "execComm"+ , totalReferrals :: !(Maybe Double) -- ^ "totalReferrals"+ , totalTurnover :: !(Maybe Double) -- ^ "totalTurnover"+ , totalComm :: !(Maybe Double) -- ^ "totalComm"+ , payoutPcnt :: !(Maybe Double) -- ^ "payoutPcnt"+ , pendingPayout :: !(Maybe Double) -- ^ "pendingPayout"+ , timestamp :: !(Maybe DateTime) -- ^ "timestamp"+ , referrerAccount :: !(Maybe Double) -- ^ "referrerAccount"+ } deriving (Show, Eq, Generic)++instance FromJSON RespAffiliate++data RespAnnouncement = RespAnnouncement+ { id :: !Double -- ^ /Required/ "id"+ , link :: !(Maybe Text) -- ^ "link"+ , title :: !(Maybe Text) -- ^ "title"+ , content :: !(Maybe Text) -- ^ "content"+ , date :: !(Maybe DateTime) -- ^ "date"+ } deriving (Show, Eq, Generic)++instance FromJSON RespAnnouncement++data RespChat = RespChat+ { id :: !(Maybe Double) -- ^ "id"+ , date :: !DateTime -- ^ /Required/ "date"+ , user :: !Text -- ^ /Required/ "user"+ , message :: !Text -- ^ /Required/ "message"+ , html :: !Text -- ^ /Required/ "html"+ , fromBot :: !(Maybe Bool) -- ^ "fromBot"+ , channelID :: !(Maybe Double) -- ^ "channelID"+ } deriving (Show, Eq, Generic)++instance FromJSON RespChat++data RespConnectedUsers = RespConnectedUsers+ { users :: !(Maybe Int) -- ^ "users"+ , bots :: !(Maybe Int) -- ^ "bots"+ } deriving (Show, Eq, Generic)++instance FromJSON RespConnectedUsers++data RespExecution = RespExecution+ { execID :: !Text -- ^ /Required/ "execID"+ , orderID :: !(Maybe Text) -- ^ "orderID"+ , clOrdID :: !(Maybe Text) -- ^ "clOrdID"+ , clOrdLinkID :: !(Maybe Text) -- ^ "clOrdLinkID"+ , account :: !(Maybe Double) -- ^ "account"+ , symbol :: !(Maybe Symbol) -- ^ "symbol"+ , side :: !(Maybe Side) -- ^ "side"+ , lastQty :: !(Maybe Double) -- ^ "lastQty"+ , lastPx :: !(Maybe Double) -- ^ "lastPx"+ , underlyingLastPx :: !(Maybe Double) -- ^ "underlyingLastPx"+ , lastMkt :: !(Maybe Text) -- ^ "lastMkt"+ , lastLiquidityInd :: !(Maybe Text) -- ^ "lastLiquidityInd"+ , simpleOrderQty :: !(Maybe Double) -- ^ "simpleOrderQty"+ , orderQty :: !(Maybe Double) -- ^ "orderQty"+ , price :: !(Maybe Double) -- ^ "price"+ , displayQty :: !(Maybe Double) -- ^ "displayQty"+ , stopPx :: !(Maybe Double) -- ^ "stopPx"+ , pegOffsetValue :: !(Maybe Double) -- ^ "pegOffsetValue"+ , pegPriceType :: !(Maybe Text) -- ^ "pegPriceType"+ , currency :: !(Maybe Currency) -- ^ "currency"+ , settlCurrency :: !(Maybe Currency) -- ^ "settlCurrency"+ , execType :: !(Maybe Text) -- ^ "execType"+ , ordType :: !(Maybe Text) -- ^ "ordType"+ , timeInForce :: !(Maybe Text) -- ^ "timeInForce"+ , execInst :: !(Maybe Text) -- ^ "execInst"+ , contingencyType :: !(Maybe Text) -- ^ "contingencyType"+ , exDestination :: !(Maybe Text) -- ^ "exDestination"+ , ordStatus :: !(Maybe Text) -- ^ "ordStatus"+ , triggered :: !(Maybe Text) -- ^ "triggered"+ , workingIndicator :: !(Maybe Bool) -- ^ "workingIndicator"+ , ordRejReason :: !(Maybe Text) -- ^ "ordRejReason"+ , simpleLeavesQty :: !(Maybe Double) -- ^ "simpleLeavesQty"+ , leavesQty :: !(Maybe Double) -- ^ "leavesQty"+ , simpleCumQty :: !(Maybe Double) -- ^ "simpleCumQty"+ , cumQty :: !(Maybe Double) -- ^ "cumQty"+ , avgPx :: !(Maybe Double) -- ^ "avgPx"+ , commission :: !(Maybe Double) -- ^ "commission"+ , tradePublishIndicator :: !(Maybe Text) -- ^ "tradePublishIndicator"+ , multiLegReportingType :: !(Maybe Text) -- ^ "multiLegReportingType"+ , text :: !(Maybe Text) -- ^ "text"+ , trdMatchID :: !(Maybe Text) -- ^ "trdMatchID"+ , execCost :: !(Maybe Double) -- ^ "execCost"+ , execComm :: !(Maybe Double) -- ^ "execComm"+ , homeNotional :: !(Maybe Double) -- ^ "homeNotional"+ , foreignNotional :: !(Maybe Double) -- ^ "foreignNotional"+ , transactTime :: !(Maybe DateTime) -- ^ "transactTime"+ , timestamp :: !(Maybe DateTime) -- ^ "timestamp"+ } deriving (Show, Eq, Generic)++instance FromJSON RespExecution++data RespFunding = RespFunding+ { timestamp :: !DateTime -- ^ /Required/ "timestamp"+ , symbol :: !Text -- ^ /Required/ "symbol"+ , fundingInterval :: !(Maybe DateTime) -- ^ "fundingInterval"+ , fundingRate :: !(Maybe Double) -- ^ "fundingRate"+ , fundingRateDaily :: !(Maybe Double) -- ^ "fundingRateDaily"+ } deriving (Show, Eq, Generic)++instance FromJSON RespFunding++data RespInstrument = RespInstrument+ { symbol :: !Symbol -- ^ /Required/ "symbol"+ , rootSymbol :: !(Maybe Currency) -- ^ "rootSymbol"+ , state :: !(Maybe Text) -- ^ "state"+ , typ :: !(Maybe Text) -- ^ "typ"+ , listing :: !(Maybe DateTime) -- ^ "listing"+ , front :: !(Maybe DateTime) -- ^ "front"+ , expiry :: !(Maybe DateTime) -- ^ "expiry"+ , settle :: !(Maybe DateTime) -- ^ "settle"+ , relistInterval :: !(Maybe DateTime) -- ^ "relistInterval"+ , inverseLeg :: !(Maybe Text) -- ^ "inverseLeg"+ , sellLeg :: !(Maybe Text) -- ^ "sellLeg"+ , buyLeg :: !(Maybe Text) -- ^ "buyLeg"+ , positionCurrency :: !(Maybe Currency) -- ^ "positionCurrency"+ , underlying :: !(Maybe Currency) -- ^ "underlying"+ , quoteCurrency :: !(Maybe Currency) -- ^ "quoteCurrency"+ , underlyingSymbol :: !(Maybe Text) -- ^ "underlyingSymbol"+ , reference :: !(Maybe Text) -- ^ "reference"+ , referenceSymbol :: !(Maybe Text) -- ^ "referenceSymbol"+ , calcInterval :: !(Maybe DateTime) -- ^ "calcInterval"+ , publishInterval :: !(Maybe DateTime) -- ^ "publishInterval"+ , publishTime :: !(Maybe DateTime) -- ^ "publishTime"+ , maxOrderQty :: !(Maybe Double) -- ^ "maxOrderQty"+ , maxPrice :: !(Maybe Double) -- ^ "maxPrice"+ , lotSize :: !(Maybe Double) -- ^ "lotSize"+ , tickSize :: !(Maybe Double) -- ^ "tickSize"+ , multiplier :: !(Maybe Double) -- ^ "multiplier"+ , settlCurrency :: !(Maybe Currency) -- ^ "settlCurrency"+ , underlyingToPositionMultiplier :: !(Maybe Double) -- ^ "underlyingToPositionMultiplier"+ , underlyingToSettleMultiplier :: !(Maybe Double) -- ^ "underlyingToSettleMultiplier"+ , quoteToSettleMultiplier :: !(Maybe Double) -- ^ "quoteToSettleMultiplier"+ , isQuanto :: !(Maybe Bool) -- ^ "isQuanto"+ , isInverse :: !(Maybe Bool) -- ^ "isInverse"+ , initMargin :: !(Maybe Double) -- ^ "initMargin"+ , maintMargin :: !(Maybe Double) -- ^ "maintMargin"+ , riskLimit :: !(Maybe Double) -- ^ "riskLimit"+ , riskStep :: !(Maybe Double) -- ^ "riskStep"+ , limit :: !(Maybe Double) -- ^ "limit"+ , capped :: !(Maybe Bool) -- ^ "capped"+ , taxed :: !(Maybe Bool) -- ^ "taxed"+ , deleverage :: !(Maybe Bool) -- ^ "deleverage"+ , makerFee :: !(Maybe Double) -- ^ "makerFee"+ , takerFee :: !(Maybe Double) -- ^ "takerFee"+ , settlementFee :: !(Maybe Double) -- ^ "settlementFee"+ , insuranceFee :: !(Maybe Double) -- ^ "insuranceFee"+ , fundingBaseSymbol :: !(Maybe Text) -- ^ "fundingBaseSymbol"+ , fundingQuoteSymbol :: !(Maybe Text) -- ^ "fundingQuoteSymbol"+ , fundingPremiumSymbol :: !(Maybe Text) -- ^ "fundingPremiumSymbol"+ , fundingTimestamp :: !(Maybe DateTime) -- ^ "fundingTimestamp"+ , fundingInterval :: !(Maybe DateTime) -- ^ "fundingInterval"+ , fundingRate :: !(Maybe Double) -- ^ "fundingRate"+ , indicativeFundingRate :: !(Maybe Double) -- ^ "indicativeFundingRate"+ , rebalanceTimestamp :: !(Maybe DateTime) -- ^ "rebalanceTimestamp"+ , rebalanceInterval :: !(Maybe DateTime) -- ^ "rebalanceInterval"+ , openingTimestamp :: !(Maybe DateTime) -- ^ "openingTimestamp"+ , closingTimestamp :: !(Maybe DateTime) -- ^ "closingTimestamp"+ , sessionInterval :: !(Maybe DateTime) -- ^ "sessionInterval"+ , prevClosePrice :: !(Maybe Double) -- ^ "prevClosePrice"+ , limitDownPrice :: !(Maybe Double) -- ^ "limitDownPrice"+ , limitUpPrice :: !(Maybe Double) -- ^ "limitUpPrice"+ , bankruptLimitDownPrice :: !(Maybe Double) -- ^ "bankruptLimitDownPrice"+ , bankruptLimitUpPrice :: !(Maybe Double) -- ^ "bankruptLimitUpPrice"+ , prevTotalVolume :: !(Maybe Double) -- ^ "prevTotalVolume"+ , totalVolume :: !(Maybe Double) -- ^ "totalVolume"+ , volume :: !(Maybe Double) -- ^ "volume"+ , volume24h :: !(Maybe Double) -- ^ "volume24h"+ , prevTotalTurnover :: !(Maybe Double) -- ^ "prevTotalTurnover"+ , totalTurnover :: !(Maybe Double) -- ^ "totalTurnover"+ , turnover :: !(Maybe Double) -- ^ "turnover"+ , turnover24h :: !(Maybe Double) -- ^ "turnover24h"+ , prevPrice24h :: !(Maybe Double) -- ^ "prevPrice24h"+ , vwap :: !(Maybe Double) -- ^ "vwap"+ , highPrice :: !(Maybe Double) -- ^ "highPrice"+ , lowPrice :: !(Maybe Double) -- ^ "lowPrice"+ , lastPrice :: !(Maybe Double) -- ^ "lastPrice"+ , lastPriceProtected :: !(Maybe Double) -- ^ "lastPriceProtected"+ , lastTickDirection :: !(Maybe Text) -- ^ "lastTickDirection"+ , lastChangePcnt :: !(Maybe Double) -- ^ "lastChangePcnt"+ , bidPrice :: !(Maybe Double) -- ^ "bidPrice"+ , midPrice :: !(Maybe Double) -- ^ "midPrice"+ , askPrice :: !(Maybe Double) -- ^ "askPrice"+ , impactBidPrice :: !(Maybe Double) -- ^ "impactBidPrice"+ , impactMidPrice :: !(Maybe Double) -- ^ "impactMidPrice"+ , impactAskPrice :: !(Maybe Double) -- ^ "impactAskPrice"+ , hasLiquidity :: !(Maybe Bool) -- ^ "hasLiquidity"+ , openInterest :: !(Maybe Double) -- ^ "openInterest"+ , openValue :: !(Maybe Double) -- ^ "openValue"+ , fairMethod :: !(Maybe Text) -- ^ "fairMethod"+ , fairBasisRate :: !(Maybe Double) -- ^ "fairBasisRate"+ , fairBasis :: !(Maybe Double) -- ^ "fairBasis"+ , fairPrice :: !(Maybe Double) -- ^ "fairPrice"+ , markMethod :: !(Maybe Text) -- ^ "markMethod"+ , markPrice :: !(Maybe Double) -- ^ "markPrice"+ , indicativeTaxRate :: !(Maybe Double) -- ^ "indicativeTaxRate"+ , indicativeSettlePrice :: !(Maybe Double) -- ^ "indicativeSettlePrice"+ , settledPrice :: !(Maybe Double) -- ^ "settledPrice"+ , timestamp :: !(Maybe DateTime) -- ^ "timestamp"+ } deriving (Show, Eq, Generic)++instance FromJSON RespInstrument++data RespInsurance = RespInsurance+ { currency :: !Currency -- ^ /Required/ "currency"+ , timestamp :: !DateTime -- ^ /Required/ "timestamp"+ , walletBalance :: !(Maybe Double) -- ^ "walletBalance"+ } deriving (Show, Eq, Generic)++instance FromJSON RespInsurance++data RespLiquidation = RespLiquidation+ { orderID :: !Text -- ^ /Required/ "orderID"+ , symbol :: !(Maybe Symbol) -- ^ "symbol"+ , side :: !(Maybe Side) -- ^ "side"+ , price :: !(Maybe Double) -- ^ "price"+ , leavesQty :: !(Maybe Double) -- ^ "leavesQty"+ } deriving (Show, Eq, Generic)++instance FromJSON RespLiquidation++data RespMargin = RespMargin+ { account :: !Double -- ^ /Required/ "account"+ , currency :: !Currency -- ^ /Required/ "currency"+ , riskLimit :: !(Maybe Double) -- ^ "riskLimit"+ , prevState :: !(Maybe Text) -- ^ "prevState"+ , state :: !(Maybe Text) -- ^ "state"+ , action :: !(Maybe Text) -- ^ "action"+ , amount :: !(Maybe Double) -- ^ "amount"+ , pendingCredit :: !(Maybe Double) -- ^ "pendingCredit"+ , pendingDebit :: !(Maybe Double) -- ^ "pendingDebit"+ , confirmedDebit :: !(Maybe Double) -- ^ "confirmedDebit"+ , prevRealisedPnl :: !(Maybe Double) -- ^ "prevRealisedPnl"+ , prevUnrealisedPnl :: !(Maybe Double) -- ^ "prevUnrealisedPnl"+ , grossComm :: !(Maybe Double) -- ^ "grossComm"+ , grossOpenCost :: !(Maybe Double) -- ^ "grossOpenCost"+ , grossOpenPremium :: !(Maybe Double) -- ^ "grossOpenPremium"+ , grossExecCost :: !(Maybe Double) -- ^ "grossExecCost"+ , grossMarkValue :: !(Maybe Double) -- ^ "grossMarkValue"+ , riskValue :: !(Maybe Double) -- ^ "riskValue"+ , taxableMargin :: !(Maybe Double) -- ^ "taxableMargin"+ , initMargin :: !(Maybe Double) -- ^ "initMargin"+ , maintMargin :: !(Maybe Double) -- ^ "maintMargin"+ , sessionMargin :: !(Maybe Double) -- ^ "sessionMargin"+ , targetExcessMargin :: !(Maybe Double) -- ^ "targetExcessMargin"+ , varMargin :: !(Maybe Double) -- ^ "varMargin"+ , realisedPnl :: !(Maybe Double) -- ^ "realisedPnl"+ , unrealisedPnl :: !(Maybe Double) -- ^ "unrealisedPnl"+ , indicativeTax :: !(Maybe Double) -- ^ "indicativeTax"+ , unrealisedProfit :: !(Maybe Double) -- ^ "unrealisedProfit"+ , syntheticMargin :: !(Maybe Double) -- ^ "syntheticMargin"+ , walletBalance :: !(Maybe Double) -- ^ "walletBalance"+ , marginBalance :: !(Maybe Double) -- ^ "marginBalance"+ , marginBalancePcnt :: !(Maybe Double) -- ^ "marginBalancePcnt"+ , marginLeverage :: !(Maybe Double) -- ^ "marginLeverage"+ , marginUsedPcnt :: !(Maybe Double) -- ^ "marginUsedPcnt"+ , excessMargin :: !(Maybe Double) -- ^ "excessMargin"+ , excessMarginPcnt :: !(Maybe Double) -- ^ "excessMarginPcnt"+ , availableMargin :: !(Maybe Double) -- ^ "availableMargin"+ , withdrawableMargin :: !(Maybe Double) -- ^ "withdrawableMargin"+ , timestamp :: !(Maybe DateTime) -- ^ "timestamp"+ , grossLastValue :: !(Maybe Double) -- ^ "grossLastValue"+ , commission :: !(Maybe Double) -- ^ "commission"+ } deriving (Show, Eq, Generic)++instance FromJSON RespMargin++data RespNotification = RespNotification+ { _id :: !(Maybe Int) -- ^ "id"+ , _date :: !DateTime -- ^ /Required/ "date"+ , _title :: !Text -- ^ /Required/ "title"+ , _body :: !Text -- ^ /Required/ "body"+ , _ttl :: !Int -- ^ /Required/ "ttl"+ , _type :: !(Maybe NotificationType) -- ^ "type"+ , _closable :: !(Maybe Bool) -- ^ "closable"+ , _persist :: !(Maybe Bool) -- ^ "persist"+ , _waitForVisibility :: !(Maybe Bool) -- ^ "waitForVisibility"+ , _sound :: !(Maybe Text) -- ^ "sound"+ } deriving (Show, Eq, Generic)++instance FromJSON RespNotification where+ parseJSON = genericParseJSON opts+ where+ opts = defaultOptions {fieldLabelModifier = drop 1}++data RespOrder = RespOrder+ { orderID :: !Text -- ^ /Required/ "orderID"+ , clOrdID :: !(Maybe Text) -- ^ "clOrdID"+ , clOrdLinkID :: !(Maybe Text) -- ^ "clOrdLinkID"+ , account :: !(Maybe Double) -- ^ "account"+ , symbol :: !(Maybe Symbol) -- ^ "symbol"+ , side :: !(Maybe Side) -- ^ "side"+ , simpleOrderQty :: !(Maybe Double) -- ^ "simpleOrderQty"+ , orderQty :: !(Maybe Double) -- ^ "orderQty"+ , price :: !(Maybe Double) -- ^ "price"+ , displayQty :: !(Maybe Double) -- ^ "displayQty"+ , stopPx :: !(Maybe Double) -- ^ "stopPx"+ , pegOffsetValue :: !(Maybe Double) -- ^ "pegOffsetValue"+ , pegPriceType :: !(Maybe Text) -- ^ "pegPriceType"+ , currency :: !(Maybe Currency) -- ^ "currency"+ , settlCurrency :: !(Maybe Currency) -- ^ "settlCurrency"+ , ordType :: !(Maybe Text) -- ^ "ordType"+ , timeInForce :: !(Maybe Text) -- ^ "timeInForce"+ , execInst :: !(Maybe Text) -- ^ "execInst"+ , contingencyType :: !(Maybe Text) -- ^ "contingencyType"+ , exDestination :: !(Maybe Text) -- ^ "exDestination"+ , ordStatus :: !(Maybe Text) -- ^ "ordStatus"+ , triggered :: !(Maybe Text) -- ^ "triggered"+ , workingIndicator :: !(Maybe Bool) -- ^ "workingIndicator"+ , ordRejReason :: !(Maybe Text) -- ^ "ordRejReason"+ , simpleLeavesQty :: !(Maybe Double) -- ^ "simpleLeavesQty"+ , leavesQty :: !(Maybe Double) -- ^ "leavesQty"+ , simpleCumQty :: !(Maybe Double) -- ^ "simpleCumQty"+ , cumQty :: !(Maybe Double) -- ^ "cumQty"+ , avgPx :: !(Maybe Double) -- ^ "avgPx"+ , multiLegReportingType :: !(Maybe Text) -- ^ "multiLegReportingType"+ , text :: !(Maybe Text) -- ^ "text"+ , transactTime :: !(Maybe DateTime) -- ^ "transactTime"+ , timestamp :: !(Maybe DateTime) -- ^ "timestamp"+ } deriving (Show, Eq, Generic)++instance FromJSON RespOrder++data RespOrderBookL2 = RespOrderBookL2+ { symbol :: !Symbol -- ^ /Required/ "symbol"+ , id :: !Double -- ^ /Required/ "id"+ , side :: !Side -- ^ /Required/ "side"+ , size :: !(Maybe Double) -- ^ "size"+ , price :: !(Maybe Double) -- ^ "price"+ } deriving (Show, Eq, Generic)++instance FromJSON RespOrderBookL2++data RespOrderBook10 = RespOrderBook10+ { symbol :: !Symbol+ , timestamp :: !Text+ , asks :: !(Vector (Vector Double))+ , bids :: !(Vector (Vector Double))+ } deriving (Eq, Show, Generic)++instance FromJSON RespOrderBook10++data RespPosition = RespPosition+ { account :: !Double -- ^ /Required/ "account"+ , symbol :: !Symbol -- ^ /Required/ "symbol"+ , currency :: !Currency -- ^ /Required/ "currency"+ , underlying :: !(Maybe Currency) -- ^ "underlying"+ , quoteCurrency :: !(Maybe Currency) -- ^ "quoteCurrency"+ , commission :: !(Maybe Double) -- ^ "commission"+ , initMarginReq :: !(Maybe Double) -- ^ "initMarginReq"+ , maintMarginReq :: !(Maybe Double) -- ^ "maintMarginReq"+ , riskLimit :: !(Maybe Double) -- ^ "riskLimit"+ , leverage :: !(Maybe Double) -- ^ "leverage"+ , crossMargin :: !(Maybe Bool) -- ^ "crossMargin"+ , deleveragePercentile :: !(Maybe Double) -- ^ "deleveragePercentile"+ , rebalancedPnl :: !(Maybe Double) -- ^ "rebalancedPnl"+ , prevRealisedPnl :: !(Maybe Double) -- ^ "prevRealisedPnl"+ , prevUnrealisedPnl :: !(Maybe Double) -- ^ "prevUnrealisedPnl"+ , prevClosePrice :: !(Maybe Double) -- ^ "prevClosePrice"+ , openingTimestamp :: !(Maybe DateTime) -- ^ "openingTimestamp"+ , openingQty :: !(Maybe Double) -- ^ "openingQty"+ , openingCost :: !(Maybe Double) -- ^ "openingCost"+ , openingComm :: !(Maybe Double) -- ^ "openingComm"+ , openOrderBuyQty :: !(Maybe Double) -- ^ "openOrderBuyQty"+ , openOrderBuyCost :: !(Maybe Double) -- ^ "openOrderBuyCost"+ , openOrderBuyPremium :: !(Maybe Double) -- ^ "openOrderBuyPremium"+ , openOrderSellQty :: !(Maybe Double) -- ^ "openOrderSellQty"+ , openOrderSellCost :: !(Maybe Double) -- ^ "openOrderSellCost"+ , openOrderSellPremium :: !(Maybe Double) -- ^ "openOrderSellPremium"+ , execBuyQty :: !(Maybe Double) -- ^ "execBuyQty"+ , execBuyCost :: !(Maybe Double) -- ^ "execBuyCost"+ , execSellQty :: !(Maybe Double) -- ^ "execSellQty"+ , execSellCost :: !(Maybe Double) -- ^ "execSellCost"+ , execQty :: !(Maybe Double) -- ^ "execQty"+ , execCost :: !(Maybe Double) -- ^ "execCost"+ , execComm :: !(Maybe Double) -- ^ "execComm"+ , currentTimestamp :: !(Maybe DateTime) -- ^ "currentTimestamp"+ , currentQty :: !(Maybe Double) -- ^ "currentQty"+ , currentCost :: !(Maybe Double) -- ^ "currentCost"+ , currentComm :: !(Maybe Double) -- ^ "currentComm"+ , realisedCost :: !(Maybe Double) -- ^ "realisedCost"+ , unrealisedCost :: !(Maybe Double) -- ^ "unrealisedCost"+ , grossOpenCost :: !(Maybe Double) -- ^ "grossOpenCost"+ , grossOpenPremium :: !(Maybe Double) -- ^ "grossOpenPremium"+ , grossExecCost :: !(Maybe Double) -- ^ "grossExecCost"+ , isOpen :: !(Maybe Bool) -- ^ "isOpen"+ , markPrice :: !(Maybe Double) -- ^ "markPrice"+ , markValue :: !(Maybe Double) -- ^ "markValue"+ , riskValue :: !(Maybe Double) -- ^ "riskValue"+ , homeNotional :: !(Maybe Double) -- ^ "homeNotional"+ , foreignNotional :: !(Maybe Double) -- ^ "foreignNotional"+ , posState :: !(Maybe Text) -- ^ "posState"+ , posCost :: !(Maybe Double) -- ^ "posCost"+ , posCost2 :: !(Maybe Double) -- ^ "posCost2"+ , posCross :: !(Maybe Double) -- ^ "posCross"+ , posInit :: !(Maybe Double) -- ^ "posInit"+ , posComm :: !(Maybe Double) -- ^ "posComm"+ , posLoss :: !(Maybe Double) -- ^ "posLoss"+ , posMargin :: !(Maybe Double) -- ^ "posMargin"+ , posMaint :: !(Maybe Double) -- ^ "posMaint"+ , posAllowance :: !(Maybe Double) -- ^ "posAllowance"+ , taxableMargin :: !(Maybe Double) -- ^ "taxableMargin"+ , initMargin :: !(Maybe Double) -- ^ "initMargin"+ , maintMargin :: !(Maybe Double) -- ^ "maintMargin"+ , sessionMargin :: !(Maybe Double) -- ^ "sessionMargin"+ , targetExcessMargin :: !(Maybe Double) -- ^ "targetExcessMargin"+ , varMargin :: !(Maybe Double) -- ^ "varMargin"+ , realisedGrossPnl :: !(Maybe Double) -- ^ "realisedGrossPnl"+ , realisedTax :: !(Maybe Double) -- ^ "realisedTax"+ , realisedPnl :: !(Maybe Double) -- ^ "realisedPnl"+ , unrealisedGrossPnl :: !(Maybe Double) -- ^ "unrealisedGrossPnl"+ , longBankrupt :: !(Maybe Double) -- ^ "longBankrupt"+ , shortBankrupt :: !(Maybe Double) -- ^ "shortBankrupt"+ , taxBase :: !(Maybe Double) -- ^ "taxBase"+ , indicativeTaxRate :: !(Maybe Double) -- ^ "indicativeTaxRate"+ , indicativeTax :: !(Maybe Double) -- ^ "indicativeTax"+ , unrealisedTax :: !(Maybe Double) -- ^ "unrealisedTax"+ , unrealisedPnl :: !(Maybe Double) -- ^ "unrealisedPnl"+ , unrealisedPnlPcnt :: !(Maybe Double) -- ^ "unrealisedPnlPcnt"+ , unrealisedRoePcnt :: !(Maybe Double) -- ^ "unrealisedRoePcnt"+ , simpleQty :: !(Maybe Double) -- ^ "simpleQty"+ , simpleCost :: !(Maybe Double) -- ^ "simpleCost"+ , simpleValue :: !(Maybe Double) -- ^ "simpleValue"+ , simplePnl :: !(Maybe Double) -- ^ "simplePnl"+ , simplePnlPcnt :: !(Maybe Double) -- ^ "simplePnlPcnt"+ , avgCostPrice :: !(Maybe Double) -- ^ "avgCostPrice"+ , avgEntryPrice :: !(Maybe Double) -- ^ "avgEntryPrice"+ , breakEvenPrice :: !(Maybe Double) -- ^ "breakEvenPrice"+ , marginCallPrice :: !(Maybe Double) -- ^ "marginCallPrice"+ , liquidationPrice :: !(Maybe Double) -- ^ "liquidationPrice"+ , bankruptPrice :: !(Maybe Double) -- ^ "bankruptPrice"+ , timestamp :: !(Maybe DateTime) -- ^ "timestamp"+ , lastPrice :: !(Maybe Double) -- ^ "lastPrice"+ , lastValue :: !(Maybe Double) -- ^ "lastValue"+ } deriving (Show, Eq, Generic)++instance FromJSON RespPosition++data RespQuote = RespQuote+ { timestamp :: !DateTime -- ^ /Required/ "timestamp"+ , symbol :: !Symbol -- ^ /Required/ "symbol"+ , bidSize :: !(Maybe Double) -- ^ "bidSize"+ , bidPrice :: !(Maybe Double) -- ^ "bidPrice"+ , askPrice :: !(Maybe Double) -- ^ "askPrice"+ , askSize :: !(Maybe Double) -- ^ "askSize"+ } deriving (Show, Eq, Generic)++instance FromJSON RespQuote++data RespSettlement = RespSettlement+ { timestamp :: !DateTime -- ^ /Required/ "timestamp"+ , symbol :: !Symbol -- ^ /Required/ "symbol"+ , settlementType :: !(Maybe Text) -- ^ "settlementType"+ , settledPrice :: !(Maybe Double) -- ^ "settledPrice"+ , bankrupt :: !(Maybe Double) -- ^ "bankrupt"+ , taxBase :: !(Maybe Double) -- ^ "taxBase"+ , taxRate :: !(Maybe Double) -- ^ "taxRate"+ } deriving (Show, Eq, Generic)++instance FromJSON RespSettlement++data RespTrade = RespTrade+ { timestamp :: !DateTime -- ^ /Required/ "timestamp"+ , symbol :: !Symbol -- ^ /Required/ "symbol"+ , side :: !(Maybe Side) -- ^ "side"+ , size :: !(Maybe Double) -- ^ "size"+ , price :: !(Maybe Double) -- ^ "price"+ , tickDirection :: !(Maybe Text) -- ^ "tickDirection"+ , trdMatchID :: !(Maybe Text) -- ^ "trdMatchID"+ , grossValue :: !(Maybe Double) -- ^ "grossValue"+ , homeNotional :: !(Maybe Double) -- ^ "homeNotional"+ , foreignNotional :: !(Maybe Double) -- ^ "foreignNotional"+ } deriving (Show, Eq, Generic)++instance FromJSON RespTrade++-- TODO: TradeBin+data RespTransaction = RespTransaction+ { transactID :: !Text -- ^ /Required/ "transactID"+ , account :: !(Maybe Double) -- ^ "account"+ , currency :: !(Maybe Currency) -- ^ "currency"+ , transactType :: !(Maybe Text) -- ^ "transactType"+ , amount :: !(Maybe Double) -- ^ "amount"+ , fee :: !(Maybe Double) -- ^ "fee"+ , transactStatus :: !(Maybe Text) -- ^ "transactStatus"+ , address :: !(Maybe Text) -- ^ "address"+ , tx :: !(Maybe Text) -- ^ "tx"+ , text :: !(Maybe Text) -- ^ "text"+ , transactTime :: !(Maybe DateTime) -- ^ "transactTime"+ , timestamp :: !(Maybe DateTime) -- ^ "timestamp"+ } deriving (Show, Eq, Generic)++instance FromJSON RespTransaction++data RespWallet = RespWallet+ { account :: !Double -- ^ /Required/ "account"+ , currency :: !Currency -- ^ /Required/ "currency"+ , prevDeposited :: !(Maybe Double) -- ^ "prevDeposited"+ , prevWithdrawn :: !(Maybe Double) -- ^ "prevWithdrawn"+ , prevTransferIn :: !(Maybe Double) -- ^ "prevTransferIn"+ , prevTransferOut :: !(Maybe Double) -- ^ "prevTransferOut"+ , prevAmount :: !(Maybe Double) -- ^ "prevAmount"+ , prevTimestamp :: !(Maybe DateTime) -- ^ "prevTimestamp"+ , deltaDeposited :: !(Maybe Double) -- ^ "deltaDeposited"+ , deltaWithdrawn :: !(Maybe Double) -- ^ "deltaWithdrawn"+ , deltaTransferIn :: !(Maybe Double) -- ^ "deltaTransferIn"+ , deltaTransferOut :: !(Maybe Double) -- ^ "deltaTransferOut"+ , deltaAmount :: !(Maybe Double) -- ^ "deltaAmount"+ , deposited :: !(Maybe Double) -- ^ "deposited"+ , withdrawn :: !(Maybe Double) -- ^ "withdrawn"+ , transferIn :: !(Maybe Double) -- ^ "transferIn"+ , transferOut :: !(Maybe Double) -- ^ "transferOut"+ , amount :: !(Maybe Double) -- ^ "amount"+ , pendingCredit :: !(Maybe Double) -- ^ "pendingCredit"+ , pendingDebit :: !(Maybe Double) -- ^ "pendingDebit"+ , confirmedDebit :: !(Maybe Double) -- ^ "confirmedDebit"+ , timestamp :: !(Maybe DateTime) -- ^ "timestamp"+ , addr :: !(Maybe Text) -- ^ "addr"+ , script :: !(Maybe Text) -- ^ "script"+ , withdrawalLock :: !(Maybe (Vector Text)) -- ^ "withdrawalLock"+ } deriving (Show, Eq, Generic)++instance FromJSON RespWallet++data Response+ = Aff (TABLE RespAffiliate)+ | Ann (TABLE RespAnnouncement)+ | C (TABLE RespChat)+ | CU (TABLE RespConnectedUsers)+ | Exe (TABLE RespExecution)+ | F (TABLE RespFunding)+ | I (TABLE RespInstrument)+ | Insu (TABLE RespInsurance)+ | L (TABLE RespLiquidation)+ | M (TABLE RespMargin)+ | N (TABLE RespNotification)+ | O (TABLE RespOrder)+ | OB (TABLE RespOrderBookL2)+ | OB10 (TABLE RespOrderBook10)+ | P (TABLE RespPosition)+ | Q (TABLE RespQuote)+ | Setl (TABLE RespSettlement)+ | T (TABLE RespTrade)+ -- | TB (TABLE RespTradeBin)+ | TX (TABLE RespTransaction)+ | W (TABLE RespWallet)+ | Status STATUS+ | Info INFO+ | Error ERROR+ deriving (Eq, Show, Generic)++instance FromJSON Response where+ parseJSON =+ withObject "Response" $ \o -> do+ kind <- o .:? "table"+ success <- o .:? "success"+ info <- o .:? "info"+ error <- o .:? "error"+ case (kind :: Maybe Text) of+ Just "affiliate" ->+ Aff <$> genericParseJSON opts (Object o)+ Just "announcement" ->+ Ann <$> genericParseJSON opts (Object o)+ Just "chat" ->+ C <$> genericParseJSON opts (Object o)+ Just "connected" ->+ CU <$> genericParseJSON opts (Object o)+ Just "execution" ->+ Exe <$> genericParseJSON opts (Object o)+ Just "funding" ->+ F <$> genericParseJSON opts (Object o)+ Just "instrument" ->+ I <$> genericParseJSON opts (Object o)+ Just "insurance" ->+ Insu <$>+ genericParseJSON opts (Object o)+ Just "liquidation" ->+ L <$> genericParseJSON opts (Object o)+ Just "margin" ->+ M <$> genericParseJSON opts (Object o)+ Just "order" ->+ O <$> genericParseJSON opts (Object o)+ Just "orderBookL2" ->+ OB <$> genericParseJSON opts (Object o)+ Just "orderBook10" ->+ OB10 <$>+ genericParseJSON opts (Object o)+ Just "position" ->+ P <$> genericParseJSON opts (Object o)+ Just "privateNotifications" ->+ N <$> genericParseJSON opts (Object o)+ Just "publicNotifications" ->+ N <$> genericParseJSON opts (Object o)+ Just "quote" ->+ Q <$> genericParseJSON opts (Object o)+ Just "settlement" ->+ Setl <$>+ genericParseJSON opts (Object o)+ Just "trade" ->+ T <$> genericParseJSON opts (Object o)+ Just "transact" ->+ TX <$> genericParseJSON opts (Object o)+ Just "wallet" ->+ W <$> genericParseJSON opts (Object o)+ Just _ ->+ fail+ "Cannot parse response: the kind of the response is not supported"+ Nothing ->+ case (success :: Maybe Bool) of+ Just _ ->+ Status <$> parseJSON (Object o)+ Nothing ->+ case (info :: Maybe Text) of+ Just _ ->+ Info <$>+ parseJSON (Object o)+ Nothing ->+ case (error :: Maybe Text) of+ Just _ ->+ Error <$>+ parseJSON+ (Object o)+ Nothing ->+ fail+ "Cannot parse response: unknown response format"+ where+ opts =+ defaultOptions+ { fieldLabelModifier = drop 1+ , sumEncoding = UntaggedValue+ }
+ lib/BitMEXClient/Wrapper.hs view
@@ -0,0 +1,7 @@+module BitMEXClient.Wrapper+ ( module X+ ) where++import BitMEXClient.Wrapper.API as X+import BitMEXClient.Wrapper.Logging as X+import BitMEXClient.Wrapper.Types as X
+ lib/BitMEXClient/Wrapper/API.hs view
@@ -0,0 +1,266 @@+module BitMEXClient.Wrapper.API+ ( makeRequest+ , connect+ , withConnectAndSubscribe+ , sign+ , makeTimestamp+ , getMessage+ , sendMessage+ ) where++import BitMEX+ ( AuthApiKeyApiKey (..)+ , AuthApiKeyApiNonce (..)+ , AuthApiKeyApiSignature (..)+ , BitMEXConfig (..)+ , BitMEXRequest (..)+ , MimeResult+ , MimeType+ , MimeUnrender+ , ParamBody (..)+ , Produces+ , addAuthMethod+ , dispatchMime+ , paramsBodyL+ , paramsQueryL+ , setHeader+ )+import BitMEX.Logging+import BitMEXClient.CustomPrelude+import BitMEXClient.WebSockets.Types+ ( Command (..)+ , Message (..)+ , Response (..)+ , Symbol+ , Topic (..)+ )+import BitMEXClient.Wrapper.Logging+import BitMEXClient.Wrapper.Types+import Data.ByteArray+ ( ByteArrayAccess+ )+import qualified Data.ByteString.Char8 as BC+ ( pack+ , unpack+ )+import Data.ByteString.Conversion+ ( toByteString'+ )+import qualified Data.ByteString.Lazy as LBS+ ( append+ )+import qualified Data.ByteString.Lazy.Char8 as LBC+ ( pack+ , unpack+ )+import qualified Data.Text as T (pack)+import qualified Data.Text.Lazy as LT+ ( toStrict+ )+import qualified Data.Text.Lazy.Encoding as LT+ ( decodeUtf8+ )+import Data.Vector (fromList)++------------------------------------------------------------+-- HELPERS++-- | Create a signature for the request.+sign ::+ (ByteArrayAccess a)+ => a+ -> BitMEXReader (Digest SHA256)+sign body = do+ secret <- asks privateKey+ return . hmacGetDigest . hmac secret $ body++makeRESTConfig :: BitMEXReader BitMEXConfig+makeRESTConfig = do+ env <- asks environment+ logCxt <- asks logContext+ path <-+ asks pathREST >>= \p ->+ return $+ case p of+ Nothing -> "/api/v1"+ Just x -> x+ let base = (LBC.pack . show) env+ logExecContext = asks logExecContext+ return+ BitMEXConfig+ { configHost = LBS.append base path+ , configUserAgent =+ "swagger-haskell-http-client/1.0.0"+ , configLogExecWithContext = logExecContext+ , configLogContext = logCxt+ , configAuthMethods = []+ , configValidateAuthMethods = True+ }++-- | Convenience function to generate a timestamp+-- for the signature of the request.+makeTimestamp :: (RealFrac a) => a -> Int+makeTimestamp = floor . (* 1000000)++------------------------------------------------------------+-- REST++-- | Prepare, authenticate and dispatch a request+-- via the auto-generated BitMEX REST API.+makeRequest ::+ ( Produces req accept+ , MimeUnrender accept res+ , MimeType contentType+ )+ => BitMEXRequest req contentType res accept+ -> BitMEXReader (MimeResult res)+makeRequest req@BitMEXRequest {..} = do+ pub <- asks publicKey+ logCxt <- asks logContext+ time <- liftIO $ makeTimestamp <$> getPOSIXTime+ config0 <-+ makeRESTConfig >>=+ liftIO . return . withLoggingBitMEXConfig logCxt+ let verb = filter (/= '"') $ show rMethod+ let query = rParams ^. paramsQueryL+ sig <-+ case rParams ^. paramsBodyL of+ ParamBodyBL lbs ->+ sign+ (BC.pack+ (verb <> "/api/v1" <>+ (LBC.unpack . head) rUrlPath <>+ BC.unpack (renderQuery True query) <>+ show time <>+ LBC.unpack lbs))+ ParamBodyB bs ->+ sign+ (BC.pack+ (verb <> "/api/v1" <>+ (LBC.unpack . head) rUrlPath <>+ BC.unpack (renderQuery True query) <>+ show time <>+ BC.unpack bs))+ _ ->+ sign+ (BC.pack+ (verb <> "/api/v1" <>+ (LBC.unpack . head) rUrlPath <>+ BC.unpack (renderQuery True query) <>+ show time))+ let new =+ setHeader+ req+ [("api-expires", toByteString' time)]+ config =+ config0 `addAuthMethod`+ AuthApiKeyApiSignature ((T.pack . show) sig) `addAuthMethod`+ AuthApiKeyApiNonce "" `addAuthMethod`+ AuthApiKeyApiKey pub+ mgr <-+ asks manager >>= \m ->+ case m of+ Nothing ->+ liftIO $ newManager tlsManagerSettings+ Just x -> return x+ liftIO $ dispatchMime mgr config new++------------------------------------------------------------+-- WebSocket++-- | Establish connection to the BitMEX WebSocket API,+-- authenticate the user and subscribe to the provided topics.+withConnectAndSubscribe ::+ BitMEXWrapperConfig+ -> [Topic Symbol]+ -> ClientApp a+ -> IO a+withConnectAndSubscribe config@BitMEXWrapperConfig {..} ts app = do+ let base = (drop 8 . show) environment+ path =+ case pathWS of+ Nothing -> "/realtime"+ Just x -> x+ withSocketsDo $+ runSecureClient base 443 (LBC.unpack path) $ \c -> do+ time <- makeTimestamp <$> getPOSIXTime+ sig <-+ runReaderT+ (run (sign+ (BC.pack+ ("GET" +++ "/realtime" ++ show time))))+ config+ sendMessage+ c+ AuthKey+ [ String publicKey+ , toJSON time+ , (toJSON . show) sig+ ]+ sendMessage c Subscribe ts+ app c++-- | Establish connection to the BitMEX WebSocket API.+connect :: BitMEXWrapperConfig -> BitMEXApp () -> IO ()+connect initConfig@BitMEXWrapperConfig {..} app = do+ let base = (drop 8 . show) environment+ path =+ case pathWS of+ Nothing -> "/realtime"+ Just x -> x+ config <-+ return $+ withLoggingBitMEXWrapper logContext initConfig+ withSocketsDo $+ runSecureClient base 443 (LBC.unpack path) $ \conn -> do+ runReaderT (run (app conn)) config++-- | Receive a message from the WebSocket connection and parse it.+getMessage ::+ Connection+ -> BitMEXWrapperConfig+ -> IO (Maybe Response)+getMessage conn config = do+ msg <- receiveData conn+ runConfigLogWithExceptions "WebSocket" config $ do+ case (decode msg :: Maybe Response) of+ Nothing -> do+ errorLog msg+ return Nothing+ Just r -> do+ case r of+ P _ -> do+ log' "Positions" msg+ return (Just r)+ OB10 _ -> do+ log' "OB10" msg+ return (Just r)+ Exe _ -> do+ log' "Execution" msg+ return (Just r)+ O _ -> do+ log' "Order" msg+ return (Just r)+ M _ -> do+ log' "Margin" msg+ return (Just r)+ Error _ -> do+ errorLog msg+ return (Just r)+ _ -> do+ log' "WebSocket" msg+ return (Just r)+ where+ log' s msg =+ _log s levelInfo $ (LT.toStrict . LT.decodeUtf8) msg+ errorLog msg =+ _log "WebSocket Error" levelError $+ (LT.toStrict . LT.decodeUtf8) msg++-- | Send a message to the WebSocket connection.+sendMessage ::+ (ToJSON a) => Connection -> Command -> [a] -> IO ()+sendMessage conn comm topics =+ sendTextData conn $+ encode $ Message {op = comm, args = fromList topics}
+ lib/BitMEXClient/Wrapper/Logging.hs view
@@ -0,0 +1,47 @@+module BitMEXClient.Wrapper.Logging+ ( withLoggingBitMEXWrapper+ , withLoggingBitMEXConfig+ , runConfigLog+ , runConfigLogWithExceptions+ ) where++import BitMEX.Core+ ( BitMEXConfig (..)+ )+import BitMEX.Logging+import BitMEXClient.CustomPrelude+import BitMEXClient.Wrapper.Types+import Data.Text (Text)++-- | Add a logging environment and an executor to a BitMEXWrapperConfig.+withLoggingBitMEXWrapper :: LogContext -> BitMEXWrapperConfig -> BitMEXWrapperConfig+withLoggingBitMEXWrapper context config =+ config+ { logExecContext = runDefaultLogExecWithContext+ , logContext = context+ }++-- | Add a logging environment and an executor to a BitMEXConfig.+withLoggingBitMEXConfig :: LogContext -> BitMEXConfig -> BitMEXConfig+withLoggingBitMEXConfig context config =+ config+ { configLogExecWithContext = runDefaultLogExecWithContext+ , configLogContext = context+ }++-- | Run the BitMEXWrapperConfig's executor on the config's+-- log environment.+runConfigLog ::+ MonadIO m => BitMEXWrapperConfig -> LogExec m+runConfigLog config =+ logExecContext config (logContext config)++-- | Run the BitMEXWrapperConfig's executor on the config's+-- log environment with exceptions.+runConfigLogWithExceptions ::+ (MonadCatch m, MonadIO m)+ => Text+ -> BitMEXWrapperConfig+ -> LogExec m+runConfigLogWithExceptions src config =+ runConfigLog config . logExceptions src
+ lib/BitMEXClient/Wrapper/Types.hs view
@@ -0,0 +1,50 @@+module BitMEXClient.Wrapper.Types+ ( BitMEXWrapperConfig(..)+ , BitMEXReader(..)+ , Environment(..)+ , BitMEXApp+ ) where++import BitMEX+ ( LogContext+ , LogExecWithContext+ )+import BitMEXClient.CustomPrelude+import qualified Data.ByteString as BS+ ( ByteString+ )+import qualified Data.ByteString.Lazy as LBS+ ( ByteString+ )+import Data.Text (Text)++data Environment+ = MainNet+ | TestNet+ deriving (Eq)++instance Show Environment where+ show MainNet = "https://www.bitmex.com"+ show TestNet = "https://testnet.bitmex.com"++data BitMEXWrapperConfig = BitMEXWrapperConfig+ { environment :: !Environment+ , pathREST :: !(Maybe LBS.ByteString)+ , pathWS :: !(Maybe LBS.ByteString)+ , manager :: !(Maybe Manager)+ , publicKey :: !Text+ , privateKey :: !BS.ByteString+ , logExecContext :: !LogExecWithContext+ , logContext :: !LogContext+ }++newtype BitMEXReader a = BitMEXReader+ { run :: (ReaderT BitMEXWrapperConfig IO) a+ } deriving ( Applicative+ , Functor+ , Monad+ , MonadIO+ , MonadReader BitMEXWrapperConfig+ )++type BitMEXApp a = Connection -> BitMEXReader a