From Backtest to Production: Implementing Live Cryptocurrency Trading with DolphinDB
Building a quantitative trading system is one thing — putting it into real-world execution is another. For most quant developers, the gap between a well-performing backtest and a robust live trading system is filled with API integrations, error handling, order management, and infrastructure challenges. This post walks through how DolphinDB bridges that gap for cryptocurrency trading, offering a live trading module that integrates seamlessly with its existing backtesting and simulation framework.
By leveraging DolphinDB's httpClient plugin alongside the official trading APIs of Binance and OKX , the live trading module lets you take a strategy straight from backtesting into production — with minimal code changes.
Why DolphinDB for Live Trading?
DolphinDB is a high-performance time-series database and analytics platform widely used in quantitative finance. Its built-in backtesting and simulation framework is designed to handle high-frequency data efficiently. The live trading module described here extends this framework so that backtesting, simulation, and live execution all share the same codebase and callback interface — dramatically reducing the learning curve and the risk of introducing bugs during the transition to production.
Supported Functions
The live trading module exposes four core functions:
| Module Function | Description | Notes |
|---|---|---|
submitOrder | Places an order. | Returns order IDs (orderId and newClientOrderId). All order details and error information are stored in a stream table. |
cancelOrder | Cancels an order. | No return value. All cancellation information is stored in the same stream table. |
getOpenOrders | Gets open orders. | Returns information on unfilled orders. |
getTradeDetails | Get trade details | Returns the latest 500 order records for a given asset. |
All activity — including successful orders, cancellations, network errors, and exchange API errors — is persisted to a stream table named by convention:
-
<username>Cryptocurrency_BinanceTradeDetailsfor Binance -
<username>Cryptocurrency_OKXTradeDetailsfor OKX
This makes it easy to audit order status and diagnose failures without hammering exchange APIs with repeated queries.
Prerequisites: Obtaining API Keys
Before going live, you need API credentials for your chosen exchange.
Binance
-
Register and log in to your Binance account.
-
Go to API Management in the account settings to obtain the API key and HMAC key.
If you do not want to trade with a personal account for now, you can use the Binance Spot Test Network.
-
Log in to your Binance account and sign in using a GitHub account.
-
Click Generate HMAC-SHA-256 Key to generate the required keys.
OKX
-
Register and log in to your OKX account.
-
Go to the API section in the account settings and create an API key.
-
Fill in the IP whitelist and passphrase, and select key permissions (read, withdraw, trade) as needed.
Note: If you do not have a static IP address, it is not recommended to set an IP whitelist, as IP changes may cause order failures. Also, API keys without a bound IP address will be automatically deleted after 14 days of inactivity.
- After creation, click View to obtain the API key and secret. Please store them securely.
Implementation Walkthrough
The following steps take you from a simulation strategy all the way to live execution. The full script is provided in the Appendix. For strategy development, see Stop Backtesting in Theory.
-
Compile the OKX signature plugin (Linux). For compilation details, see the plugin document.
-
Download the DolphinDBPlugin project based on your server version, and place the source code (hex2Base64-main.zip) in the root directory.
-
Run the following commands to generate .so and .txt files in the output folder.
-
Place the output/hex2Base64-main folder in the server/plugins directory.
-
cd ../DolphinDBPlugin/hex2Base64-main
export CMAKE_INSTALL_PREFIX="$(pwd)/output/$PluginName"
# Default ABI1
export CXXFLAGS="-D_GLIBCXX_USE_CXX11_ABI=0" # Specify ABI0 via environment variable
CXX=g++-8 CC=gcc-8 bash -x ./build.sh # Modify compiler version as needed
- Place the
CryptocurrencySolutionmodule in the modules directory under the home directory (getHomeDir()), move files in CryptocurrencyTrading.zip to the CryptocurrencySolution directory, and modify the OKXTradingModule.dos file based on the plugin path.
try{loadPlugin("/.../server/plugins/hex2Base64-main/PluginHex2Base64.txt")}catch(ex){print(ex)}
go
- Load the modules as follows:
use CryptocurrencySolution::simulatedTrading
use CryptocurrencySolution::CryptocurrencyTrading
go
- Retrieve and store asset precision information.
tb = CryptocurrencySolution::CryptocurrencyTrading::getAssetPrecision()
dbName = "dfs://CryptocurrencyDay"
tbName = "precision"
colNames = `symbol`symbolSource`contractType`tickSize`stepSize
colTypes = [SYMBOL,STRING,STRING,DOUBLE,DOUBLE]
if(!existsDatabase(dbName)){
db = database(dbName,RANGE,2010.01M+(0..20)*60)
}else{ db=database(dbName)}
if(!existsTable(dbName,tbName)){
createDimensionTable(db,table(1:0,colNames,colTypes),tbName)
}
loadTable(dbName,tbName).append!(tb)
- Configure account credentials in
userConfig["context"].
p = dict(STRING,ANY)
// Key information – fill in with actual keys
keyInfo = dict(STRING,ANY)
keyInfo["apiKey"] = ""
keyInfo["secretKey"] = ""
// OKX requires an additional passphrase
// keyInfo["passphrase"] = "123456"
p["keyInfo"] = keyInfo
userConfig["context"] = p
- Example: placing orders in a minute-level strategy. Orders are placed in the onBar callback. In the submitOrder function, isSim indicates whether the order is placed in live trading or simulation mode, and it must match the applied API key.
def onBar(mutable context, msg, indicator){
endTime = timestamp()
for(istock in msg.keys()){
source = msg[istock]["symbolSource"]
buyPrice=msg[istock]["close"]+10
sellPrice =msg[istock]["close"]-10
if(context["number"]< 5){
submitOrder(context["keyInfo"],(istock,source,context["tradeTime"],5,buyPrice,0.0,0.0,0.01,1,0,1,endTime),"testOrders_buy",
orderType=0, accountType ="futures",exchange = "Binance",paramDict=NULL,strategyName=context["strategyName"],isSim=true, proxy=NULL)
context["number"] += 1
}else{
submitOrder(context["keyInfo"],(istock,source,context["tradeTime"],5,sellPrice,0.0,0.0,0.01,3,0,1,endTime),"testOrders_sell",
orderType = 0, accountType = "futures",exchange = "Binance", paramDict = NULL,strategyName = context["strategyName"],isSim = true, proxy = NULL)
con}
def onOrder(mutable context,orders){}
def onTrade(mutable context,trades){text["number"] -= 1
}
}
}
def onOrder(mutable context,orders){}
def onTrade(mutable context,trades){}
Lines 8 and 12 show that the live order placement function is largely consistent with that of the backtesting plugin. InBacktest::submitOrder, the first engine parameter must be replaced with the account credential information keyInfo. Cryptocurrency exchanges support more order parameters. Extensible parameters are passed via paramDict. For details, see New Order | Binance Open Platform and POST / Place Order – OKX API Documentation.
- Submit the strategy using submitCryptoSimulatedTrading. Real-time market data will enter the engine and trigger the callbacks to execute live trading.
engine,straname_ = CryptocurrencySolution::simulatedTrading::submitCryptoSimulatedTrading(strategyName,
eventCallbacks, userConfig, overwrite = true)
Note: If you need to store live trading strategy code, use the CryptocurrencySolution::manageScripts module and add keyInfo in the unified configuration function under CryptocurrencySolution::setting, or pass keyInfo via a custom userConfig. Other usage is the same as backtesting and simulation. For details, see Stop Backtesting in Theory.
Live trading details can be obtained from the stream table (e.g., admin_testSubmitRealOrder_BinanceTradeDetails). Successful order placements and cancellations are recorded in this table. If an order fails due to network issues or API errors, the error information is also stored, making it easy to review order status or diagnose failures.
You can retrieve open orders and trade details using the following functions:
openOrders_s = getOpenOrders(keyInfo,"BTCUSDT_spot")
openOrders_f = getOpenOrders(keyInfo,"BTCUSDT_futures", accountType="futures")
tradeDetails_s = getTradeDetails(keyInfo,"BTCUSDT_spot", accountType="spot")
tradeDetails_f = getTradeDetails(keyInfo,"BTCUSDT_futures", accountType="futures")
Conclusion
Live trading is where quantitative strategies meet the real world — and the stakes are high. By using DolphinDB's live trading module, you can move from backtest to production without rewriting your strategy logic, while keeping a full audit trail of every order and error along the way. Whether you're connecting to Binance or OKX, the setup is straightforward, and the unified callback interface means less time wiring infrastructure and more time refining your strategies.