BSD-3-Clause licensed by Manuel Schneckenreither
Maintained by [email protected]
This version can be pinned in stack with:api-maker-,2296

Api Maker: Implementing APIs like never before

Api Maker let’s you implement APIs simply by implementing the class Request for each possible request. It is built on top of the Req library.

Lets say we want to implement a GET request that returns orders taken in an account. This actually implements the GET orders request of So we create a data type structure to hold what we want:

-- | GET Order(s) request
data GetOrder =
  GetOrder AccountId OrderConfig

-- | This data type defines the header options that we can send
data OrderConfig = OrderConfig
  { ids        :: Maybe [OrderId]           -- ^ List of Order IDs to retrieve
  , state      :: Maybe OrderStateFilter    -- ^ The state to filter the requested Orders by [default=PENDING]
  , instrument :: Maybe InstrumentName      -- ^ The instrument to filter the requested orders by
  , orderCount :: Maybe Int                 -- ^ The maximum number of Orders to return [default=50, maximum=500]
  , beforeID   :: Maybe OrderId             -- ^ The maximum Order ID to return. If not provided the most recent Orders in the Account are returned
  } deriving (Show, Eq, Ord, ToJSON, Generic, NFData)

and then we implement the class

instance Request OandaConfig GetOrder where
  type Method GetOrder = GET                        -- It's a GET request
  type Body GetOrder = NoReqBody                    -- We do not send a body
  type Response GetOrder = JsonResponse OrderList   -- The response is a JSON list of orders
  type Output GetOrder = OrderList                  -- We simply output the list in the `process` function without transformation
  method _ GetOrder {} = GET                        -- GET request again
  url cfg (GetOrder accId _) = ".../account/orders" -- The URL
  body _ (GetOrder _ req) = NoReqBody               -- No Body to send
  response _ GetOrder {} = jsonResponse             -- The expected response is in JSON format and is parsed using `aeson` with this function.
  option _ (GetOrder _ cfg) =                       -- What header options to send
    headerRFC3339DatetimeFormat <> configs
        configs =
          case cfg of
            OrderConfig ids state instrument count beforeID ->
              "ids"        `queryParam` fmap (T.intercalate ";") ids <>
              "state"      `queryParam` fmap show state <>
              "instrument" `queryParam` instrument <>
              "count"      `queryParam` count <>
              "beforeID"   `queryParam` beforeID

  -- Defines how to process the Response. One might change it here, e.g. filter values that cannot be specified by the request options.
  process _ GetOrder {} response = return $ responseBody response

The request State monad is then run like this:

runSessReqM cfg $ runRequests $ do
  orders <- mkReq $ GetOrder myAccountId (OrderConfig Nothing Nothing (Just "USD/EUR") (Just 100) Nothing)
  print orders

This will create the session and run the requests. All HttpExceptions errors are caught and converted to exceptions. Thus runSessReqM returns a Either HttpException a. The function mkReq is used to actually make the requests, the data-type GetOrder defines what request to make and uses the corresponding account ID and the configuration provided. The result is a list of Orders, with type OrderList.


Changelog for borl-trader

Unreleased changes