Code Examples
📋 Table of Contents
💻 Code Examples
JavaScript Examples
const https = require('https');
const crypto = require('crypto');
const { URL } = require('url');
function generateNonce(length = 16) {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
return Array.from({ length }, () => chars.charAt(Math.floor(Math.random() * chars.length))).join('');
}
function sortObject(obj) {
if (Array.isArray(obj)) {
return obj.map(sortObject);
} else if (typeof obj === 'object' && obj !== null) {
return Object.keys(obj)
.sort()
.reduce((acc, key) => {
acc[key] = sortObject(obj[key]);
return acc;
}, {});
}
return obj;
}
function flattenObject(obj, parentKey = '', result = {}) {
if (typeof obj !== 'object' || obj === null) return result;
for (const [key, value] of Object.entries(obj)) {
if (value == null) continue;
const newKey = parentKey ? `${parentKey}.${key}` : key;
if (Array.isArray(value)) {
value.forEach((v, i) => flattenObject(v, `${newKey}[${i}]`, result));
} else if (typeof value === 'object') {
flattenObject(value, newKey, result);
} else {
if (String(value).trim() !== '') result[newKey] = String(value);
}
}
return result;
}
function sortQueryParams(query) {
if (!query) return '';
const pairs = query.split('&').filter(Boolean);
const params = pairs.map(p => p.split('=')).filter(p => p.length === 2);
params.sort((a, b) => a[0].localeCompare(b[0]));
return params.map(([k, v]) => `${k}=${v}`).join('&');
}
function generateSignature(method, url, body, secret, timestamp, nonce) {
const urlObj = new URL(url);
let query = sortQueryParams(urlObj.search.slice(1));
let bodyData = '';
if (['POST', 'PUT'].includes(method.toUpperCase()) && body) {
const sortedBody = sortObject(body);
const flatBody = flattenObject(sortedBody);
bodyData = Object.entries(flatBody)
.map(([k, v]) => `${k}=${v}`)
.join('&');
}
// POST: (bodyData.isEmpty() ? "" : bodyData + "&") + "timestamp=xxx&nonce=xxx"
// GET: (query.isEmpty() ? "&" : query + "&") + "timestamp=xxx&nonce=xxx"
let signData = '';
if (method.toUpperCase() === 'GET') {
signData = (query ? `${query}&` : '&') + `timestamp=${timestamp}&nonce=${nonce}`;
} else {
signData = (bodyData ? `${bodyData}&` : '') + `timestamp=${timestamp}&nonce=${nonce}`;
}
const hmac = crypto.createHmac('sha256', secret);
hmac.update(signData);
return hmac.digest('hex');
}
function sendGetRequest(url, headers) {
return new Promise((resolve, reject) => {
const req = https.get(url, { headers }, res => {
let data = '';
res.on('data', chunk => (data += chunk));
res.on('end', () => {
console.log('状态码:', res.statusCode);
console.log('响应:', data);
resolve(data);
});
});
req.on('error', reject);
});
}
(async () => {
const apiKey = '8aB3yCz4QpW9sT7rM0dN2uE5kH1xJ6fVtLqO8bRzP3cS9yD0mG4nF7aU';
const apiSecret = 'W7eR1tF6pA2yQ3dK5sM9xL8bG4jC0vHnUoOzTqP1rE5iN2mS7uY6aZ0l';
const url = 'https://openapi.bittap.com/stapi/v1/account';
const method = 'GET';
const timestamp = Date.now();
const nonce = generateNonce();
const sign = generateSignature(method, url, null, apiSecret, timestamp, nonce);
const headers = {
'X-BT-APIKEY': apiKey,
'X-BT-SIGN': sign,
'X-BT-TS': timestamp,
'X-BT-NONCE': nonce,
};
console.log('sign:', sign);
await sendGetRequest(url, headers);
})();Java Examples
public class OpenApiDemo {
private static final String API_KEY = "";
private static final String API_SECRET = "";
private static final String BASE_URL = "https://openapi.bittap.com";
private static final String SEPARATOR = "&";
private static final String EQUALS = "=";
private static final OkHttpClient CLIENT = new OkHttpClient();
private static final ObjectMapper MAPPER = new ObjectMapper();
public static void main(String[] args) throws Exception {
// Batch place orders
testBatchCreate();
// Cancel order
testCancelOrder();
// Batch cancel
testBatchCancel();
// Query open orders
testOpenOrders();
// Query historical orders
testHisOrders();
// Query trade records
testTrades();
// Query account balance
testAccount();
}
// ----------------- API Calls -------------------
// 1. Batch place orders
public static void testBatchCreate() throws Exception {
String url = BASE_URL + "/stapi/v1/batchOrders";
ObjectNode body = MAPPER.createObjectNode();
body.put("symbol", "BTC-USDT");
ArrayNode items = body.putArray("items");
ObjectNode order1 = MAPPER.createObjectNode();
order1.put("clientOrderId", UUID.randomUUID().toString().replace("-", ""));
order1.put("type", "LIMIT");
order1.put("price", "100002");
order1.put("quantity", "0.0011");
order1.put("timeInForce", "GTC");
order1.put("selfTradePreventionMode", "EXPIRE_BOTH");
order1.put("side", "BUY");
items.add(order1);
ObjectNode order2 = MAPPER.createObjectNode();
order2.put("clientOrderId", UUID.randomUUID().toString().replace("-", ""));
order2.put("type", "LIMIT");
order2.put("price", "100002");
order2.put("quantity", "0.0011");
order2.put("timeInForce", "GTC");
order2.put("selfTradePreventionMode", "EXPIRE_BOTH");
order2.put("side", "BUY");
items.add(order2);
sendSignedPost(url, body);
}
// 2. Cancel order
public static void testCancelOrder() throws Exception {
String url = BASE_URL + "/stapi/v1/cancelOrder";
ObjectNode body = MAPPER.createObjectNode();
body.put("clientOrderId", "00295c0dadbb47a5b5a0d3fe4ad83fc9");
sendSignedPost(url, body);
}
// 3. Batch cancel orders
public static void testBatchCancel() throws Exception {
String url = BASE_URL + "/stapi/v1/batchCancelOrders";
ObjectNode body = MAPPER.createObjectNode();
body.put("symbol", "BTC-USDT");
sendSignedPost(url, body);
}
// 4. Query open orders
public static void testOpenOrders() throws Exception {
String url = BASE_URL + "/stapi/v1/openOrders";
sendSignedGet(url);
}
// 5. Query historical orders
public static void testHisOrders() throws Exception {
String url = BASE_URL + "/stapi/v1/hisOrders?symbol=BTC-USDT";
sendSignedGet(url);
}
// 6. Query trade records
public static void testTrades() throws Exception {
String url = BASE_URL + "/stapi/v1/trades?pageNumber=1&pageSize=10";
sendSignedGet(url);
}
// 7. Query account balance
public static void testAccount() throws Exception {
String url = BASE_URL + "/stapi/v1/account";
sendSignedGet(url);
}
// ----------------- Utility Methods -------------------
private static void sendSignedPost(String url, ObjectNode body) throws Exception {
long timestamp = System.currentTimeMillis();
String nonce = UUID.randomUUID().toString().replace("-", "");
// 1. Sort JSON
JsonNode sortedNode = sortJsonNode(body);
// 1. Flatten body
String bodyData = convertJsonToQueryString(sortedNode);
// 2. Concatenate signature string
String signData = (bodyData.isEmpty() ? "" : bodyData + "&") +
"timestamp=" + timestamp +
"&nonce=" + nonce;
String sign = hmacSha256Hex(signData, API_SECRET);
String bodyJson = MAPPER.writeValueAsString(body);
RequestBody requestBody = RequestBody.create(
bodyJson, MediaType.parse("application/json; charset=utf-8"));
Request request = new Request.Builder()
.url(url)
.post(requestBody)
.addHeader("X-BT-APIKEY", API_KEY)
.addHeader("X-BT-SIGN", sign)
.addHeader("X-BT-TS", String.valueOf(timestamp))
.addHeader("X-BT-NONCE", nonce)
.build();
try (Response response = CLIENT.newCall(request).execute()) {
System.out.println("POST " + url);
System.out.println("Response: " + response.code() + " " + (response.body() != null ? response.body().string() : ""));
}
}
private static void sendSignedGet(String url) throws Exception {
long timestamp = System.currentTimeMillis();
String nonce = UUID.randomUUID().toString().replace("-", "");
// Parse existing query from URL
String query = "";
int idx = url.indexOf("?");
if (idx != -1) {
query = url.substring(idx + 1);
}
query = sortQueryParams(query);
String signData = (query.isEmpty() ? "&" : query + "&") +
"timestamp=" + timestamp +
"&nonce=" + nonce;
String sign = hmacSha256Hex(signData, API_SECRET);
String finalUrl = url;
Request request = new Request.Builder()
.url(finalUrl)
.get()
.addHeader("X-BT-APIKEY", API_KEY)
.addHeader("X-BT-SIGN", sign)
.addHeader("X-BT-TS", String.valueOf(timestamp))
.addHeader("X-BT-NONCE", nonce)
.build();
try (Response response = CLIENT.newCall(request).execute()) {
System.out.println("GET " + finalUrl);
System.out.println("Response: " + response.code() + " " + (response.body() != null ? response.body().string() : ""));
}
}
private static String convertJsonToQueryString(JsonNode jsonNode) {
if (jsonNode == null || jsonNode.isNull()) {
return "";
}
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
flattenJson(jsonNode, "", params);
StringBuilder queryString = new StringBuilder();
boolean first = true;
for (Map.Entry<String, String> entry : params.toSingleValueMap().entrySet()) {
if (!first) {
queryString.append("&");
} else {
first = false;
}
queryString.append(entry.getKey()).append("=").append(entry.getValue());
}
return queryString.toString();
}
private static void flattenJson(JsonNode node, String path, MultiValueMap<String, String> params) {
if (node.isObject()) {
Iterator<Map.Entry<String, JsonNode>> fields = node.fields();
while (fields.hasNext()) {
Map.Entry<String, JsonNode> field = fields.next();
if (field.getValue().isNull()) {
continue;
}
String newPath = path.isEmpty() ? field.getKey() : path + "." + field.getKey();
flattenJson(field.getValue(), newPath, params);
}
} else if (node.isArray()) {
for (int i = 0; i < node.size(); i++) {
JsonNode jsonNode = node.get(i);
if (jsonNode.isNull() || jsonNode.isArray() && jsonNode.isEmpty()) {
continue;
}
String newPath = path + "[" + i + "]";
flattenJson(node.get(i), newPath, params);
}
} else {
// Basic type
if (node.asText().isBlank()) {
return;
}
params.add(path, node.asText());
}
}
private static String hmacSha256Hex(String data, String secret) throws Exception {
Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec keySpec = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
mac.init(keySpec);
byte[] raw = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
StringBuilder sb = new StringBuilder(raw.length * 2);
for (byte b : raw) sb.append(String.format("%02x", b));
return sb.toString();
}
private static JsonNode sortJsonNode(JsonNode node) {
try {
if (node == null || node.isNull()) return node;
if (node.isObject()) {
ObjectNode out = MAPPER.createObjectNode();
TreeMap<String, JsonNode> sorted = new TreeMap<>();
Iterator<Map.Entry<String, JsonNode>> fields = node.fields();
while (fields.hasNext()) {
Map.Entry<String, JsonNode> f = fields.next();
sorted.put(f.getKey(), sortJsonNode(f.getValue()));
}
for (Map.Entry<String, JsonNode> e : sorted.entrySet()) {
out.set(e.getKey(), e.getValue());
}
return out;
} else if (node.isArray()) {
ArrayNode arr = MAPPER.createArrayNode();
for (JsonNode el : node) arr.add(sortJsonNode(el));
return arr;
} else {
return node;
}
} catch (Exception ex) {
return node;
}
}
private static String sortQueryParams(String query) {
if (StringUtils.isBlank(query)) {
return "";
}
try {
// Use LinkedMultiValueMap to handle parameters with same key
MultiValueMap<String, String> paramMap = new LinkedMultiValueMap<>();
String[] paramPairs = query.split(SEPARATOR);
for (String pair : paramPairs) {
String[] keyValue = pair.split(EQUALS, 2);
if (keyValue.length == 2) {
paramMap.add(keyValue[0], keyValue[1]);
}
}
// Convert MultiValueMap to Map, values with same key become array
Map<String, Object> convertedMap = new LinkedHashMap<>();
for (Map.Entry<String, List<String>> entry : paramMap.entrySet()) {
String key = entry.getKey();
List<String> values = entry.getValue();
if (values.size() == 1) {
convertedMap.put(key, values.get(0));
} else {
convertedMap.put(key, values);
}
}
// Use Jackson to convert Map to JsonNode
JsonNode jsonNode = MAPPER.valueToTree(convertedMap);
// Sort JsonNode
JsonNode sortedJsonNode = sortJsonNode(jsonNode);
// Convert sorted JsonNode to query string
return convertJsonToQueryString(sortedJsonNode);
} catch (Exception e) {
return query;
}
}
}Python Examples
import requests
import json
import time
import hmac
import hashlib
import uuid
from urllib.parse import urlparse, parse_qsl, urlencode
### =====================================
### API CONFIGURATION
### =====================================
API_KEY = "" # Please fill in your API Key
API_SECRET = "" # Please fill in your API Secret
BASE_URL = "https://openapi.bittap.com"
### =====================================
### UTILITY FUNCTIONS
### =====================================
def sort_dict(d):
"""Recursively sort dictionary keys alphabetically"""
if not isinstance(d, dict):
return d
sorted_dict = {}
for k in sorted(d.keys()):
v = d[k]
if isinstance(v, dict):
sorted_dict[k] = sort_dict(v)
elif isinstance(v, list):
sorted_dict[k] = [sort_dict(i) if isinstance(i, dict) else i for i in v]
else:
sorted_dict[k] = v
return sorted_dict
def flatten_dict(d, parent_key='', sep='.'):
"""Flatten nested JSON into dotted key format"""
items = []
for k, v in d.items():
new_key = parent_key + sep + k if parent_key else k
if v is None:
continue
if isinstance(v, dict):
items.extend(flatten_dict(v, new_key, sep=sep).items())
elif isinstance(v, list):
for i, item in enumerate(v):
if item is None or (isinstance(item, list) and not item):
continue
if isinstance(item, dict):
items.extend(flatten_dict(item, f"{new_key}[{i}]", sep=sep).items())
else:
if str(item).strip():
items.append((f"{new_key}[{i}]", str(item)))
else:
if str(v).strip():
items.append((new_key, str(v)))
return dict(items)
def dict_to_query_string(d):
"""Convert dictionary to query string format"""
return '&'.join(f"{k}={v}" for k, v in d.items())
def sort_query_params(query_string):
"""Sort URL query parameters alphabetically"""
if not query_string:
return ""
params = parse_qsl(query_string, keep_blank_values=True)
params.sort(key=lambda x: x[0])
return urlencode(params)
def hmac_sha256_hex(data, secret):
"""Generate HMAC-SHA256 signature"""
return hmac.new(secret.encode('utf-8'), data.encode('utf-8'), hashlib.sha256).hexdigest()
### =====================================
### SIGNED REQUEST FUNCTIONS
### =====================================
def send_signed_post(endpoint, body):
"""Send a signed POST request"""
url = BASE_URL + endpoint
timestamp = int(time.time() * 1000)
nonce = uuid.uuid4().hex
sorted_body = sort_dict(body)
flat_body = flatten_dict(sorted_body)
body_data = dict_to_query_string(flat_body)
sign_data = (body_data + "&" if body_data else "") + f"timestamp={timestamp}&nonce={nonce}"
signature = hmac_sha256_hex(sign_data, API_SECRET)
headers = {
"X-BT-APIKEY": API_KEY,
"X-BT-SIGN": signature,
"X-BT-TS": str(timestamp),
"X-BT-NONCE": nonce,
"Content-Type": "application/json; charset=utf-8"
}
print(f"\n--- POST {url} ---")
print("Request Body:", json.dumps(body))
print("Signature Data:", sign_data)
print("Signature:", signature)
resp = requests.post(url, headers=headers, json=body)
print(f"Response [{resp.status_code}]:", resp.text)
def send_signed_get(endpoint):
"""Send a signed GET request"""
url = BASE_URL + endpoint
timestamp = int(time.time() * 1000)
nonce = uuid.uuid4().hex
parsed_url = urlparse(url)
sorted_query = sort_query_params(parsed_url.query)
sign_data = (sorted_query + "&" if sorted_query else "&") + f"timestamp={timestamp}&nonce={nonce}"
signature = hmac_sha256_hex(sign_data, API_SECRET)
headers = {
"X-BT-APIKEY": API_KEY,
"X-BT-SIGN": signature,
"X-BT-TS": str(timestamp),
"X-BT-NONCE": nonce
}
print(f"\n--- GET {url} ---")
print("Signature Data:", sign_data)
print("Signature:", signature)
resp = requests.get(url, headers=headers)
print(f"Response [{resp.status_code}]:", resp.text)
### =====================================
### API TEST FUNCTIONS
### =====================================
def test_batch_create():
endpoint = "/stapi/v1/batchOrders"
body = {
"symbol": "BTC-USDT",
"items": [
{
"clientOrderId": uuid.uuid4().hex,
"type": "LIMIT",
"price": "100002",
"quantity": "0.0011",
"timeInForce": "GTC",
"selfTradePreventionMode": "EXPIRE_BOTH",
"side": "BUY"
}
]
}
send_signed_post(endpoint, body)
def test_cancel_order():
endpoint = "/stapi/v1/cancelOrder"
body = {"clientOrderId": "00295c0dadbb47a5b5a0d3fe4ad83fc9"}
send_signed_post(endpoint, body)
def test_batch_cancel():
endpoint = "/stapi/v1/batchCancelOrders"
body = {"symbol": "BTC-USDT"}
send_signed_post(endpoint, body)
def test_open_orders():
send_signed_get("/stapi/v1/openOrders")
def test_his_orders():
send_signed_get("/stapi/v1/hisOrders?symbol=BTC-USDT")
def test_trades():
send_signed_get("/stapi/v1/trades?pageNumber=1&pageSize=10")
def test_account():
send_signed_get("/stapi/v1/account")
### =====================================
### MAIN EXECUTION ENTRY
### =====================================
if __name__ == "__main__":
if not API_KEY or not API_SECRET:
print("❌ ERROR: API_KEY and API_SECRET must be configured!")
else:
test_batch_create()
test_cancel_order()
test_batch_cancel()
test_open_orders()
test_his_orders()
test_trades()
test_account()
🔍 Frequently Asked Questions
Q: How to generate random string?
A: You can use the following methods:
- JavaScript: Use
crypto.randomUUID()or custom function - Java: Use
UUID.randomUUID().toString().replace("-", "")
Q: How to handle nested objects?
A: Use recursive function to flatten nested objects, use dot notation to separate hierarchy levels.
Q: How to represent array parameters?
A: Array parameters use square bracket indexing, e.g., items[0].price=100&items[1].price=200
Q: What to do if signature verification fails?
A: Please check:
- Whether parameter sorting is correct
- Whether timestamp is within valid period
- Whether nonce is reused
- Whether API Secret is correct
Q: Which programming languages are supported?
A: We provide complete examples for JavaScript and Java, other languages can refer to the signature algorithm for self-implementation.
📚 Dependency Information
JavaScript Dependencies
{
"dependencies": {
"crypto": "^1.0.1"
}
}Java Dependencies
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.11.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>6.0.11</version>
</dependency>
</dependencies>🚀 Quick Start
- Choose programming language: Select JavaScript or Java based on your project needs
- Configure environment: Install necessary dependency packages
- Set up keys: Configure your API Key and Secret
- Run examples: Execute example code to verify signature algorithm
- Integrate into project: Integrate signature logic into your actual project
📖 More Resources
- Authentication & Security - Learn detailed signature rules
- Trading Interface - View complete trading interface documentation
- Market Data Interface - Get market data interfaces
- API Reference - View complete API documentation
Last updated on: