Coinbase Logo

Language and region

Developing a simple reference Order and Execution Management System (OEMS) for use with Coinbase Prime

TL;DR: This blog post outlines the development of an open-source Order and Execution Management System (OEMS) for use by Coinbase Prime clients, highlighting its features, installation process, and usage guidelines.

By Jeff Curry

Product

, October 5, 2023

Coinbase Blog

For institutions that build applications for cryptocurrency trading, Coinbase stands out for its powerful and user-friendly Coinbase Prime API, which covers the entire trade lifecycle. The Coinbase Prime API empowers Prime clients to tailor their applications to specific trading objectives, ranging from introducing broker-style buy-and-hold strategies to the most advanced algorithmic and quantitative trading techniques. In this blog post, the Coinbase Prime API is used to power a simple and lightweight Order and Execution Management System (OEMS) application, which provides a terminal user interface for efficiently placing and managing trades. It may be used in conjunction with the Prime UI or entirely by itself.

The OEMS application is a particularly useful tool for traders who want a simplified user experience with a focused feature set that helps to make trading more efficient. Developed in Go, a language known for its robust performance, conciseness, and parallel process management, this application utilizes many of Coinbase Prime's most important APIs across REST, FIX, and WebSockets. Using an easy to follow installation process, Prime clients can easily place, preview, and manage orders over FIX and REST, visualize a maintained order book, incorporate fat finger protection, and even trade with One Cancels the Other (OCO) orders.

The entire reference application is released as an open source project at github.com/coinbase-samples/trader-shell-go. As you navigate this post, you'll find links directing you to specific sections of the application. 

Functionality

Designed for efficiency, this application operates directly from a Go IDE or terminal. Upon successfully running the application, the user is brought to a main menu with four options. Selecting an option allows the user to progress to the next section where they may provide additional input or return to the previous screen. The available options at this stage are: 

  1. Place an order

  2. Subscribe to an order book

  3. Manage open orders and view closed orders and balances

  4. Manage one-cancels-the-other (OCO) orders

To illustrate the application flow, refer to the flow diagram below:

pasted image 0 (19)

Individual elements of the above application flow are explained below:

  • Order input: Facilitates both market and limit order placement over FIX

  • Order preview: Before placing an order, users may incorporate an order preview over REST to view calculated slippage and commission

  • Fat finger protection: Checks on total notional order size and prevents overly marketable limit orders

  • Market data: Displays a maintained order book for any product supported by Prime

  • Manage open orders: Visualize and cancel open orders

  • View closed orders: View recently closed orders

  • Asset-level balances: View individual asset balances

  • OCO order management: View and cancel OCO orders 

High level architecture

FIX 

Upon initializing the sample application, a connection is immediately opened to Coinbase Prime via FIX and is persisted for the duration of the application’s runtime. This is achieved through logic that reads in a config.yaml file, which also includes several options that may be customized by users of the application. The application automatically sends a signature request to Coinbase based on the credentials specified in the creds.json file: API key (or access key), API Secret (or signing key), passphrase, Service Account ID, and Portfolio ID. All of these values may be pulled directly from the Prime UI. The signing function is included below to highlight a proper way to form a signature when connection to Coinbase Prime via FIX: 

func (app *TradeApp) sign(timestamp, msgType, seqNum, accessKey, targetCompID, passphrase string) string {
message := []byte(timestamp + msgType + seqNum + accessKey + targetCompID + passphrase)
hmac256 := hmac.New(sha256.New, []byte(app.APISecret))
hmac256.Write(message)
signature := base64.StdEncoding.EncodeToString(hmac256.Sum(nil))
return signature
}

Using the open connection, all near real-time order statuses are automatically transmitted over FIX from Coinbase to the user’s local process and displayed in the application. Any order that is submitted via FIX by the user will also use this open connection, so no additional authentication processes sent to the FIX admin are required. Should the application disconnect from the FIX server, it will automatically attempt to reconnect, and upon successful reconnection, play back any missed FIX status updates. An example of real time order status updates is shown here: 

pasted image 0 (20)

FIX is used by the application for order placement and real-time order status tracking, as opposed to over REST. Standard FIX package logic is stored within core/fix.go, which also includes many references to QuickFIX, a full featured open source messaging library for the FIX protocol. QuickFIX is used to handle several required FIX functions. FIX utilizes many constants called tags, and an included dictionary.go file contains all utilized tags across the application. This was chosen to improve code readability for anyone looking to educate themselves on the standard FIX implementation and additional functionalities added in this particular application. Otherwise, the implementation is very similar to standard QuickFIX examples provided by the developer.

REST

REST requests are used by the sample application for generating order previews, checking balances, maintaining open orders, and viewing closed orders. All REST requests are submitted and processed at the time at which that option is selected within the application (e.g. when requesting an order preview). Responses from any REST endpoints are not kept in memory; rather, additional REST requests are used to retrieve information like order status and balances, so all displayed data will be up to date and not subject to caching issues. Further, no data is cached after the application is closed. An example of a formatted response for closed orders, is shown here: 

pasted image 0 (21)

Functions that utilize REST are contained within core/rest.go. The response provided from the Coinbase Prime REST API is typically a JSON array of JSON objects, and depending on the endpoint, may contain a large amount of information. Accordingly, all responses processed by this application are interpreted and simplified like as seen above.

WebSockets

At the time of writing, the primary means by which Coinbase Prime clients may consume near real-time market data is by subscribing to the L2 data WebSocket channel. This channel provides a constant stream of bids and asks for a given product, sending new updates to the client roughly every 500ms. For users looking for an easy to interpret indication of “the current price”, this is far more data than what is needed. Further, in order to format this data into a fully maintained order book, the data must be ingested and processed. A full blog detailing how to maintain an order book is written here. For the purposes of this article, the high level overview of converting the L2 WebSocket to an order book involves four steps: 

1) Starting a WebSocket to begin receiving price data 

2) Ingesting the data and storing it in some kind of database object

3) Updating this database object every time an update arrives at a particular price level 

4) Displaying the lines of the database object in a user interface

The full code for starting, stopping, and handling the WebSocket loop is available in core/websocket.go. The full code for building and maintaining the order book is available in core/orderbook.go.

Price Data 

As previously mentioned, the L2 data channel is an excellent way to construct an order book, but not nearly as good for a quick and simple read on the current price of a given product. For certain functionality built into this application, the Coinbase Exchange API is used in order to capture the price of a given product and refresh this data occasionally. Because this is a REST endpoint, it’s important to note that the data will be quickly outdated; however, this is still a strong representation of how to handle a system internally when looking for “good enough” price approximations. 

Within this application, this price data is used to assist in a few operations, which include building a representation of fat finger protection, as well as triggering stops used by OCO orders. Fortunately, requests for price do not require authentication. The logic for generating this request for the Coinbase Exchange API is shown below: 

func fetchPrice(productID string) (float64, error) {
url := "https://api.exchange.coinbase.com/products/" + productID + "/ticker"
resp, err := http.Get(url)
if err != nil {
return 0, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return 0, fmt.Errorf("non-200 response code when fetching price for %s: %d", productID, resp.StatusCode)
}
var data PriceData
decoder := json.NewDecoder(resp.Body)
if err = decoder.Decode(&data); err != nil {
return 0, fmt.Errorf("failed to decode price data for %s: %v", productID, err)
}
priceCache[productID] = data
return strconv.ParseFloat(data.Price, 64)
}
Because this application is meant to provide a demonstration of a reference price, select products are maintained by a variable called supportedProducts within core/create.go. Further, a goroutine is used to refresh this price every 10 seconds, however this may be adjusted in the same file. Adjusting this timer will also adjust how often fat finger protection calculations are refreshed, as well as how often stop orders are triggered. 

Fat Finger Protection

Fat finger protection typically refers to code that prevents undesirable orders from being submitted, such as an order that adds an extra zero to the buy price or misses a digit from the sell price. Mistyped order values, particularly in the heat of trading, can result in significant financial losses. This application has two forms of fat finger protection implemented: max notional protection and overly marketable limit protection. Please note, these protections are for example only, as are all other features in this post. These may not be effective or relevant for users. There is the possibility that these protections will fail, and should never be used in a production environment with real funds, as real funds may be lost. 

Max notional protection refers to the prevention of an order size over a certain notional limit. Notional is defined as the total value of the order, in USD terms. For example, if placing a buy order for ETH-USD when ETH is trading at 1.6K USD and max notional protection set to 50k USD, the application would stop an order from being placed in a case where the notional size of the order exceeds 50k USD; if this was done with a base quantity order, that would be around 31 ETH. The maximum notional value may be adjusted at the top of core/create.go.

Additional potential protection comes in the form of overly marketable limit orders. This is by default set to 5%, but this can be customized in core/dictionary.go. For example, if ETH is currently trading at 1.5k USD, and the user accidentally inputs a limit buy price of 1.6k USD, the order will not be submitted.

Fat finger protection works due to the goroutine that handles capturing price data. With a relatively recent record of the product’s current price, the code calculates notional order size at the time of order placement, as well as compares the stored price against the user-provided limit price. It is important to note again that because of the REST response used for capturing price data, both forms of fat finger protection require consideration around intense market volatility. Additionally, it is important to add relevant products to the function that captures price data. Should you place a trade for a product that is not included, the application will send you a warning letting you know to add it.

Screenshot 2023-10-05 at 11.16.30 AM

One Cancels the Other (OCO) Orders

A one cancels the other (OCO) order is a unique order type that creates two linked limit orders. When one of the two orders is executed, the other is canceled. This is useful in several scenarios, however one of the most important is introducing the idea of stop orders to Coinbase Prime. Currently, this order type does not exist out of the box, so this application has created a stop order by combining the aforementioned price reference functionality with a market order.

An example OCO order is as follows: a trader wishes to place an OCO order for ETH-USD, which is currently trading at 1.6k USD. She wants to buy ETH at 1.55k USD, but in the case of a breakout, also wants to buy at 1.65k USD as well. Her current expectation is that the price will fall, but also wants a hedge in case the price rises. In reality, she is correct – ETH eventually falls to the price of 1.55k USD, and her limit buy is executed. Simultaneously, her no longer needed 1.65k USD limit buy is canceled. 

The OCO functionality built into this application supports buys and sells in both directions. The unmarketable order will always be a limit order, while the overly marketable order will be a latent market order waiting to be stopped. OCO orders may also be canceled by using the OCO manager option from the main menu.

There are several functions involved in the orchestration of OCO orders. One such function is the processing of “stop orders”, or the overly marketable order that is being held for execution, in the case that the underlying asset price passes the marked stop order. For more information on OCO orders, please refer to core/price.go.

Usage

Usability was a key consideration in designing this application. As such, the installation is relatively straightforward, and regular usage of the application is designed to be highly functional without a steep learning curve. 

1. Clone the repository and change directories with the following commands: 

git clone https://github.com/coinbase-samples/trader-shell-go
cd trader-shell-go

2. Ensure all dependencies are available with the following command:

go mod download

3. Copy and rename the example files that you will use in the next step to provide your credentials:

cp creds_ex.json creds.json
cp config_ex.yaml config.yaml

4. Provide your SVC_ACCOUNTID on line 24 of config.yaml, as well as your API credentials and Portfolio ID to creds.json

5. For FIX to operate, you will need a valid certificate, which you may import directly if you are familiar, or by running this to generate a new certificate:

openssl s_client -showcerts -connect fix.prime.coinbase.com:4198 < /dev/null | openssl x509 -outform PEM > fix-prime.coinbase.com.pem

6. Finally, to run the application, type the following command:

go run cmd/cli/* config.yaml

Upon running the application, you should immediately see an OnCreate message and then an "S >>" to admin, followed by a "R <<" response back from the admin stating you are connected to Coinbase Prime over FIX. The most logical scenario in which this step fails is that the certificate was not properly built, so please make sure you have properly completed that step. If this step was done properly, confirm your API credentials are added to the relevant files, and that the names of the files are changed to remove “_ex”.

After successfully getting a response back from the server, you should see a large ASCII art welcoming you to the application, followed by a menu with the following choices:

1. trade input

2. market data

3. order manager

4. oco manager

Type a number and hit enter to make a choice.

Trade input is where you can place trades over FIX, or generate an order preview over REST. The required values for order submission are as follows:

product orderType buyOrSell baseQuantity

If the orderType is specified as `lim`, the user will need to provide their limit price as the fourth parameter, before `baseQuantity`, as follows:

product orderType buyOrSell limitPrice baseQuantity

Examples of common orders are shown below:

eth-usd mkt b 0.001
eth-usd lim b 1400 0.001
ltc-usd lim s 100 15 -p
btc-usd lim b 15000 0.001 -oco 30000

These orders translate to the following:

  1. I wish to market buy 0.001 ETH on the ETH-USD market

  2. I wish to limit buy 0.001 ETH at 1.4k USD on the ETH-USD market

  3. I wish to preview a limit sell order for 15 LTC at 100 USD

  4. I wish to place an OCO order for BTC on the BTC-USD market where my underlying limit price is 15k USD and my upper trigger stop buy is 30k USD.

The -p flag allows you to preview the order (over REST) and then you can hit g to submit it

The -oco flag is a complex order type where you create a limit with a stop loss (second value) — the stop loss value is recorded and stored in main menu option 4. If either order is executed, the other order is canceled. As stated earlier, in order for OCO mode to properly function, make sure that the product you are interested in is included in the supportedProducts variable within create.go.

Fat finger protection will prevent you from placing a market order that costs over a certain notional limit, or a limit order that is more than 5% worse than the current market price. Like OCO orders, this functionality requires including additional products to the supportedProducts variable within create.go, as well as adjusting MaxOrderSize, also within create.go.

Market data will allow you to subscribe to any available Coinbase Prime product and visualize its order book up to 9 levels deep, e.g.:

eth-usd 5

This screen will also present your available balance so that you may quickly exit and place a trade with the relevant trade information that this screen provides.

Order manager provides insight into open, closed, and balance data. You are able to cancel open orders directly from the open orders screen by naming an order by number and including -c, i.e. 1 -c

OCO manager provides visual data for currently open OCO orders. You may cancel an OCO order directly from this screen by naming an order by number and including -c, i.e. 1 -c

This piece showcases the architecture and featureset of a Go-based reference OEMS application built for Coinbase Prime, with special features like fat finger protection and One Cancels the Other (OCO) orders. Clients integrating with Coinbase Prime are encouraged to test this application and use it as a reference when developing their own applications.

--

Disclaimer: this application should never be used in any production environment, and any real funds used in this application may be lost. Additionally, this information is provided for informational purposes only and is not investment advice. This is not a recommendation to buy or sell digital assets or to employ a particular investment strategy, codes, or APIs. Coinbase makes no representation on the accuracy, suitability, or validity of any information provided herein.

This material is for informational purposes only and is not (i) an offer, or solicitation of an offer, to invest in, or to buy or sell, any interests or shares, or to participate in any investment or trading strategy, or (ii) intended to provide accounting, legal, or tax advice, or investment recommendations. No representation or warranty is made, expressed or implied, with respect to the accuracy or completeness of the information or to the future performance of any digital asset, financial instrument or other market or economic measure. The information is believed to be current as of the date indicated and may not be updated or otherwise revised to reflect information that subsequently became available or a change in circumstances after the date of publication. Investing in cryptocurrency comes with risk. Prior results do not guarantee future performance. Readers should consult their advisors before making any investment decision. Any references to third parties do not imply Coinbase's endorsement, or approval of any third-party websites or their content. This material is the property of Coinbase, Inc. Any use, review, retransmission, distribution, or reproduction of these materials, in whole or in part, is strictly prohibited in any form without the express written approval of Coinbase. Coinbase, Inc. is licensed to engage in virtual currency business activity by the New York State Department of Financial Services. © 2023 Coinbase, Inc. All Rights Reserved. COINBASE, the C Logo, and the Wallet Logo are all trademarks of Coinbase, Inc.

Coinbase logo
Jeff Curry

About Jeff Curry

Senior Solutions Architect, STP