交易所API设计
量化交易团队经常因为API响应慢、数据不一致而导致策略失效。API不仅是技术接口,更是交易所的核心竞争力。一个设计良好的API系统能够支撑从每日50万次到5000万次的调用量,将API延迟从平均200ms降到15ms以下。
本章参考Binance、Coinbase、OKEx等头部交易所的API设计,分享交易所API设计的最佳实践,包括REST API、WebSocket、签名认证、限流、错误处理等核心内容。
1. API架构设计
1.1 整体架构
用户
↓
API Gateway (Nginx/Kong)
↓
├─→ REST API 服务集群
├─→ WebSocket 服务集群
└─→ 签名验证服务
↓
├─→ 限流服务 (Redis)
├─→ 业务服务 (订单、账户、行情)
└─→ 数据库集群 (MySQL/Redis)
1.2 API分类
| API类型 | 用途 | 认证要求 | 示例 |
|---|---|---|---|
| Public API | 公开数据 | 无需认证 | 获取K线、深度、交易记录 |
| Private API | 用户数据 | 需要签名 | 查询余额、订单历史 |
| Trade API | 交易操作 | 需要签名 | 下单、撤单 |
| WebSocket | 实时数据 | 部分需要 | 实时行情、订单更新 |
2. REST API设计
2.1 URL设计规范
遵循RESTful风格:
# 好的设计
GET /api/v1/markets # 获取所有交易对
GET /api/v1/markets/{symbol} # 获取特定交易对
GET /api/v1/orders # 获取所有订单
GET /api/v1/orders/{orderId} # 获取特定订单
POST /api/v1/orders # 创建订单
DELETE /api/v1/orders/{orderId} # 撤销订单
# 不好的设计
GET /api/v1/getMarkets # 动词冗余
POST /api/v1/order/create # 路径包含动词
GET /api/v1/deleteOrder # GET用于删除操作
2.2 Go实现示例
package api
import (
"encoding/json"
"net/http"
"strconv"
"time"
"github.com/gorilla/mux"
)
type APIServer struct {
router *mux.Router
orderSvc OrderService
accountSvc AccountService
marketSvc MarketService
authSvc AuthService
}
func NewAPIServer() *APIServer {
s := &APIServer{
router: mux.NewRouter(),
}
s.registerRoutes()
return s
}
func (s *APIServer) registerRoutes() {
// Public API
public := s.router.PathPrefix("/api/v1").Subrouter()
public.HandleFunc("/time", s.handleGetServerTime).Methods("GET")
public.HandleFunc("/markets", s.handleGetMarkets).Methods("GET")
public.HandleFunc("/markets/{symbol}", s.handleGetMarket).Methods("GET")
public.HandleFunc("/ticker/{symbol}", s.handleGetTicker).Methods("GET")
public.HandleFunc("/depth/{symbol}", s.handleGetDepth).Methods("GET")
public.HandleFunc("/trades/{symbol}", s.handleGetTrades).Methods("GET")
public.HandleFunc("/klines/{symbol}", s.handleGetKlines).Methods("GET")
// Private API (需要认证)
private := s.router.PathPrefix("/api/v1").Subrouter()
private.Use(s.authMiddleware)
private.HandleFunc("/account", s.handleGetAccount).Methods("GET")
private.HandleFunc("/balance", s.handleGetBalance).Methods("GET")
private.HandleFunc("/orders", s.handleGetOrders).Methods("GET")
private.HandleFunc("/orders/{orderId}", s.handleGetOrder).Methods("GET")
private.HandleFunc("/orders/open", s.handleGetOpenOrders).Methods("GET")
// Trade API
trade := s.router.PathPrefix("/api/v1").Subrouter()
trade.Use(s.authMiddleware)
trade.Use(s.rateLimitMiddleware)
trade.HandleFunc("/orders", s.handleCreateOrder).Methods("POST")
trade.HandleFunc("/orders/{orderId}", s.handleCancelOrder).Methods("DELETE")
trade.HandleFunc("/orders/cancel-all", s.handleCancelAllOrders).Methods("POST")
}
// ============ Public API ============
// 获取服务器时间
func (s *APIServer) handleGetServerTime(w http.ResponseWriter, r *http.Request) {
response := map[string]interface{}{
"serverTime": time.Now().UnixMilli(),
}
s.writeJSON(w, http.StatusOK, response)
}
// 获取所有交易对
func (s *APIServer) handleGetMarkets(w http.ResponseWriter, r *http.Request) {
markets := s.marketSvc.GetAllMarkets()
s.writeJSON(w, http.StatusOK, markets)
}
// 获取特定交易对信息
func (s *APIServer) handleGetMarket(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
symbol := vars["symbol"]
market, err := s.marketSvc.GetMarket(symbol)
if err != nil {
s.writeError(w, http.StatusNotFound, "MARKET_NOT_FOUND", "Market not found")
return
}
s.writeJSON(w, http.StatusOK, market)
}
// 获取Ticker
func (s *APIServer) handleGetTicker(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
symbol := vars["symbol"]
ticker, err := s.marketSvc.GetTicker(symbol)
if err != nil {
s.writeError(w, http.StatusNotFound, "SYMBOL_NOT_FOUND", "Symbol not found")
return
}
s.writeJSON(w, http.StatusOK, ticker)
}
// 获取订单簿深度
func (s *APIServer) handleGetDepth(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
symbol := vars["symbol"]
// 可选参数:limit (默认100)
limitStr := r.URL.Query().Get("limit")
limit := 100
if limitStr != "" {
if l, err := strconv.Atoi(limitStr); err == nil {
limit = l
}
}
depth, err := s.marketSvc.GetDepth(symbol, limit)
if err != nil {
s.writeError(w, http.StatusInternalServerError, "INTERNAL_ERROR", err.Error())
return
}
s.writeJSON(w, http.StatusOK, depth)
}
// 获取最近成交记录
func (s *APIServer) handleGetTrades(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
symbol := vars["symbol"]
limitStr := r.URL.Query().Get("limit")
limit := 500
if limitStr != "" {
if l, err := strconv.Atoi(limitStr); err == nil && l <= 1000 {
limit = l
}
}
trades, err := s.marketSvc.GetRecentTrades(symbol, limit)
if err != nil {
s.writeError(w, http.StatusInternalServerError, "INTERNAL_ERROR", err.Error())
return
}
s.writeJSON(w, http.StatusOK, trades)
}
// 获取K线数据
func (s *APIServer) handleGetKlines(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
symbol := vars["symbol"]
interval := r.URL.Query().Get("interval") // 1m, 5m, 15m, 1h, 1d, etc.
if interval == "" {
interval = "1m"
}
limitStr := r.URL.Query().Get("limit")
limit := 500
if limitStr != "" {
if l, err := strconv.Atoi(limitStr); err == nil {
limit = l
}
}
klines, err := s.marketSvc.GetKlines(symbol, interval, limit)
if err != nil {
s.writeError(w, http.StatusInternalServerError, "INTERNAL_ERROR", err.Error())
return
}
s.writeJSON(w, http.StatusOK, klines)
}
// ============ Private API ============
// 获取账户信息
func (s *APIServer) handleGetAccount(w http.ResponseWriter, r *http.Request) {
userID := r.Context().Value("userID").(string)
account, err := s.accountSvc.GetAccount(userID)
if err != nil {
s.writeError(w, http.StatusInternalServerError, "INTERNAL_ERROR", err.Error())
return
}
s.writeJSON(w, http.StatusOK, account)
}
// 获取余额
func (s *APIServer) handleGetBalance(w http.ResponseWriter, r *http.Request) {
userID := r.Context().Value("userID").(string)
balances, err := s.accountSvc.GetBalances(userID)
if err != nil {
s.writeError(w, http.StatusInternalServerError, "INTERNAL_ERROR", err.Error())
return
}
s.writeJSON(w, http.StatusOK, balances)
}
// 获取订单列表
func (s *APIServer) handleGetOrders(w http.ResponseWriter, r *http.Request) {
userID := r.Context().Value("userID").(string)
symbol := r.URL.Query().Get("symbol")
limitStr := r.URL.Query().Get("limit")
limit := 100
if limitStr != "" {
if l, err := strconv.Atoi(limitStr); err == nil {
limit = l
}
}
orders, err := s.orderSvc.GetOrders(userID, symbol, limit)
if err != nil {
s.writeError(w, http.StatusInternalServerError, "INTERNAL_ERROR", err.Error())
return
}
s.writeJSON(w, http.StatusOK, orders)
}
// 获取未完成订单
func (s *APIServer) handleGetOpenOrders(w http.ResponseWriter, r *http.Request) {
userID := r.Context().Value("userID").(string)
symbol := r.URL.Query().Get("symbol")
orders, err := s.orderSvc.GetOpenOrders(userID, symbol)
if err != nil {
s.writeError(w, http.StatusInternalServerError, "INTERNAL_ERROR", err.Error())
return
}
s.writeJSON(w, http.StatusOK, orders)
}
// ============ Trade API ============
// 创建订单
func (s *APIServer) handleCreateOrder(w http.ResponseWriter, r *http.Request) {
userID := r.Context().Value("userID").(string)
var req CreateOrderRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
s.writeError(w, http.StatusBadRequest, "INVALID_REQUEST", "Invalid request body")
return
}
// 验证参数
if err := s.validateOrderRequest(&req); err != nil {
s.writeError(w, http.StatusBadRequest, "INVALID_PARAMS", err.Error())
return
}
// 创建订单
order, err := s.orderSvc.CreateOrder(userID, &req)
if err != nil {
s.writeError(w, http.StatusInternalServerError, "ORDER_FAILED", err.Error())
return
}
s.writeJSON(w, http.StatusOK, order)
}
// 撤销订单
func (s *APIServer) handleCancelOrder(w http.ResponseWriter, r *http.Request) {
userID := r.Context().Value("userID").(string)
vars := mux.Vars(r)
orderID := vars["orderId"]
err := s.orderSvc.CancelOrder(userID, orderID)
if err != nil {
s.writeError(w, http.StatusInternalServerError, "CANCEL_FAILED", err.Error())
return
}
s.writeJSON(w, http.StatusOK, map[string]string{
"orderId": orderID,
"status": "canceled",
})
}
// 撤销所有订单
func (s *APIServer) handleCancelAllOrders(w http.ResponseWriter, r *http.Request) {
userID := r.Context().Value("userID").(string)
var req struct {
Symbol string `json:"symbol"`
}
json.NewDecoder(r.Body).Decode(&req)
count, err := s.orderSvc.CancelAllOrders(userID, req.Symbol)
if err != nil {
s.writeError(w, http.StatusInternalServerError, "CANCEL_FAILED", err.Error())
return
}
s.writeJSON(w, http.StatusOK, map[string]interface{}{
"canceled": count,
})
}
// ============ 辅助函数 ============
type CreateOrderRequest struct {
Symbol string `json:"symbol"`
Side string `json:"side"` // "buy" or "sell"
Type string `json:"type"` // "limit" or "market"
Price float64 `json:"price"`
Quantity float64 `json:"quantity"`
TimeInForce string `json:"timeInForce"` // "GTC", "IOC", "FOK"
}
func (s *APIServer) validateOrderRequest(req *CreateOrderRequest) error {
if req.Symbol == "" {
return fmt.Errorf("symbol is required")
}
if req.Side != "buy" && req.Side != "sell" {
return fmt.Errorf("invalid side")
}
if req.Type != "limit" && req.Type != "market" {
return fmt.Errorf("invalid type")
}
if req.Type == "limit" && req.Price <= 0 {
return fmt.Errorf("price must be positive for limit orders")
}
if req.Quantity <= 0 {
return fmt.Errorf("quantity must be positive")
}
return nil
}
// 统一响应格式
type APIResponse struct {
Success bool `json:"success"`
Data interface{} `json:"data,omitempty"`
Error *APIError `json:"error,omitempty"`
}
type APIError struct {
Code string `json:"code"`
Message string `json:"message"`
}
func (s *APIServer) writeJSON(w http.ResponseWriter, status int, data interface{}) {
w.Header().Set("Content-Type", "application/json")
w.WriteStatus(status)
response := APIResponse{
Success: true,
Data: data,
}
json.NewEncoder(w).Encode(response)
}
func (s *APIServer) writeError(w http.ResponseWriter, status int, code, message string) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
response := APIResponse{
Success: false,
Error: &APIError{
Code: code,
Message: message,
},
}
json.NewEncoder(w).Encode(response)
}
3. 签名认证
3.1 HMAC-SHA256签名
package auth
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
"net/url"
"sort"
"strings"
"time"
)
type Signer struct {
apiKey string
secretKey string
}
func NewSigner(apiKey, secretKey string) *Signer {
return &Signer{
apiKey: apiKey,
secretKey: secretKey,
}
}
// 生成签名
func (s *Signer) Sign(method, path string, params map[string]string) string {
// 1. 添加时间戳和API Key
params["timestamp"] = fmt.Sprintf("%d", time.Now().UnixMilli())
params["apiKey"] = s.apiKey
// 2. 按字母顺序排序参数
keys := make([]string, 0, len(params))
for k := range params {
keys = append(keys, k)
}
sort.Strings(keys)
// 3. 拼接查询字符串
var parts []string
for _, k := range keys {
parts = append(parts, fmt.Sprintf("%s=%s", k, url.QueryEscape(params[k])))
}
queryString := strings.Join(parts, "&")
// 4. 构造待签名字符串
// 格式:METHOD\nPATH\nQUERY_STRING
signString := fmt.Sprintf("%s\n%s\n%s", method, path, queryString)
// 5. HMAC-SHA256签名
h := hmac.New(sha256.New, []byte(s.secretKey))
h.Write([]byte(signString))
signature := hex.EncodeToString(h.Sum(nil))
return signature
}
// 验证签名
func (s *Signer) Verify(method, path string, params map[string]string, signature string) bool {
expectedSignature := s.Sign(method, path, params)
return hmac.Equal([]byte(signature), []byte(expectedSignature))
}
// 检查时间戳(防重放攻击)
func (s *Signer) ValidateTimestamp(timestamp int64) bool {
now := time.Now().UnixMilli()
// 允许5分钟的时间差
if abs(now-timestamp) > 5*60*1000 {
return false
}
return true
}
func abs(n int64) int64 {
if n < 0 {
return -n
}
return n
}
3.2 认证中间件
func (s *APIServer) authMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 1. 获取签名和时间戳
signature := r.Header.Get("X-Signature")
timestampStr := r.Header.Get("X-Timestamp")
apiKey := r.Header.Get("X-API-Key")
if signature == "" || timestampStr == "" || apiKey == "" {
s.writeError(w, http.StatusUnauthorized, "MISSING_AUTH", "Missing authentication headers")
return
}
// 2. 验证时间戳
timestamp, err := strconv.ParseInt(timestampStr, 10, 64)
if err != nil || !s.authSvc.ValidateTimestamp(timestamp) {
s.writeError(w, http.StatusUnauthorized, "INVALID_TIMESTAMP", "Invalid or expired timestamp")
return
}
// 3. 获取用户密钥
secretKey, err := s.authSvc.GetSecretKey(apiKey)
if err != nil {
s.writeError(w, http.StatusUnauthorized, "INVALID_API_KEY", "Invalid API key")
return
}
// 4. 验证签名
params := extractParams(r)
signer := auth.NewSigner(apiKey, secretKey)
if !signer.Verify(r.Method, r.URL.Path, params, signature) {
s.writeError(w, http.StatusUnauthorized, "INVALID_SIGNATURE", "Invalid signature")
return
}
// 5. 获取用户ID
userID, err := s.authSvc.GetUserID(apiKey)
if err != nil {
s.writeError(w, http.StatusUnauthorized, "INVALID_USER", "User not found")
return
}
// 6. 将用户ID放入上下文
ctx := context.WithValue(r.Context(), "userID", userID)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func extractParams(r *http.Request) map[string]string {
params := make(map[string]string)
// 从URL query参数提取
for k, v := range r.URL.Query() {
if len(v) > 0 {
params[k] = v[0]
}
}
// 从body提取(POST请求)
if r.Method == "POST" || r.Method == "PUT" {
r.ParseForm()
for k, v := range r.PostForm {
if len(v) > 0 {
params[k] = v[0]
}
}
}
return params
}
3.3 客户端使用示例
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
type ExchangeClient struct {
apiKey string
secretKey string
baseURL string
signer *auth.Signer
}
func NewExchangeClient(apiKey, secretKey, baseURL string) *ExchangeClient {
return &ExchangeClient{
apiKey: apiKey,
secretKey: secretKey,
baseURL: baseURL,
signer: auth.NewSigner(apiKey, secretKey),
}
}
// 创建订单
func (c *ExchangeClient) CreateOrder(symbol, side, orderType string, price, quantity float64) (*Order, error) {
params := map[string]string{
"symbol": symbol,
"side": side,
"type": orderType,
"price": fmt.Sprintf("%.8f", price),
"quantity": fmt.Sprintf("%.8f", quantity),
}
return c.signedRequest("POST", "/api/v1/orders", params)
}
func (c *ExchangeClient) signedRequest(method, path string, params map[string]string) (*Order, error) {
// 1. 生成签名
signature := c.signer.Sign(method, path, params)
timestamp := params["timestamp"]
// 2. 构造请求
url := c.baseURL + path
var body io.Reader
if method == "POST" {
jsonData, _ := json.Marshal(params)
body = bytes.NewBuffer(jsonData)
}
req, err := http.NewRequest(method, url, body)
if err != nil {
return nil, err
}
// 3. 添加认证头
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-API-Key", c.apiKey)
req.Header.Set("X-Timestamp", timestamp)
req.Header.Set("X-Signature", signature)
// 4. 发送请求
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
// 5. 解析响应
var result Order
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return nil, err
}
return &result, nil
}
4. WebSocket API
4.1 服务端实现
package websocket
import (
"encoding/json"
"sync"
"time"
"github.com/gorilla/websocket"
)
type WSServer struct {
clients map[*Client]bool
broadcast chan []byte
register chan *Client
unregister chan *Client
mu sync.RWMutex
}
type Client struct {
conn *websocket.Conn
send chan []byte
topics map[string]bool // 订阅的主题
mu sync.RWMutex
}
func NewWSServer() *WSServer {
return &WSServer{
clients: make(map[*Client]bool),
broadcast: make(chan []byte, 1000),
register: make(chan *Client),
unregister: make(chan *Client),
}
}
func (s *WSServer) Run() {
for {
select {
case client := <-s.register:
s.mu.Lock()
s.clients[client] = true
s.mu.Unlock()
case client := <-s.unregister:
s.mu.Lock()
if _, ok := s.clients[client]; ok {
delete(s.clients, client)
close(client.send)
}
s.mu.Unlock()
case message := <-s.broadcast:
s.mu.RLock()
for client := range s.clients {
select {
case client.send <- message:
default:
close(client.send)
delete(s.clients, client)
}
}
s.mu.RUnlock()
}
}
}
// 处理客户端连接
func (s *WSServer) HandleClient(w http.ResponseWriter, r *http.Request) {
upgrader := websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true // 生产环境应检查origin
},
}
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Println(err)
return
}
client := &Client{
conn: conn,
send: make(chan []byte, 256),
topics: make(map[string]bool),
}
s.register <- client
// 启动读写goroutine
go client.writePump()
go client.readPump(s)
}
// 读取客户端消息
func (c *Client) readPump(server *WSServer) {
defer func() {
server.unregister <- c
c.conn.Close()
}()
c.conn.SetReadDeadline(time.Now().Add(60 * time.Second))
c.conn.SetPongHandler(func(string) error {
c.conn.SetReadDeadline(time.Now().Add(60 * time.Second))
return nil
})
for {
_, message, err := c.conn.ReadMessage()
if err != nil {
break
}
// 处理订阅/取消订阅
var msg WSMessage
if err := json.Unmarshal(message, &msg); err != nil {
continue
}
switch msg.Method {
case "subscribe":
c.subscribe(msg.Params...)
case "unsubscribe":
c.unsubscribe(msg.Params...)
}
}
}
// 向客户端发送消息
func (c *Client) writePump() {
ticker := time.NewTicker(54 * time.Second)
defer func() {
ticker.Stop()
c.conn.Close()
}()
for {
select {
case message, ok := <-c.send:
c.conn.SetWriteDeadline(time.Now().Add(10 * time.Second))
if !ok {
c.conn.WriteMessage(websocket.CloseMessage, []byte{})
return
}
w, err := c.conn.NextWriter(websocket.TextMessage)
if err != nil {
return
}
w.Write(message)
// 批量发送队列中的消息
n := len(c.send)
for i := 0; i < n; i++ {
w.Write([]byte{'\n'})
w.Write(<-c.send)
}
if err := w.Close(); err != nil {
return
}
case <-ticker.C:
c.conn.SetWriteDeadline(time.Now().Add(10 * time.Second))
if err := c.conn.WriteMessage(websocket.PingMessage, nil); err != nil {
return
}
}
}
}
// 订阅主题
func (c *Client) subscribe(topics ...string) {
c.mu.Lock()
defer c.mu.Unlock()
for _, topic := range topics {
c.topics[topic] = true
}
}
// 取消订阅
func (c *Client) unsubscribe(topics ...string) {
c.mu.Lock()
defer c.mu.Unlock()
for _, topic := range topics {
delete(c.topics, topic)
}
}
// WebSocket消息格式
type WSMessage struct {
Method string `json:"method"` // subscribe, unsubscribe
Params []string `json:"params"` // 主题列表
}
// 推送消息到订阅了特定主题的客户端
func (s *WSServer) PublishToTopic(topic string, data interface{}) {
message, _ := json.Marshal(map[string]interface{}{
"topic": topic,
"data": data,
})
s.mu.RLock()
defer s.mu.RUnlock()
for client := range s.clients {
client.mu.RLock()
subscribed := client.topics[topic]
client.mu.RUnlock()
if subscribed {
select {
case client.send <- message:
default:
close(client.send)
delete(s.clients, client)
}
}
}
}
4.2 客户端使用示例
// JavaScript客户端
const ws = new WebSocket('wss://api.example.com/ws');
ws.onopen = () => {
console.log('Connected');
// 订阅ticker
ws.send(JSON.stringify({
method: 'subscribe',
params: ['ticker.BTCUSDT', 'depth.BTCUSDT']
}));
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.topic === 'ticker.BTCUSDT') {
console.log('Ticker:', data.data);
} else if (data.topic === 'depth.BTCUSDT') {
console.log('Depth:', data.data);
}
};
ws.onerror = (error) => {
console.error('WebSocket error:', error);
};
ws.onclose = () => {
console.log('Disconnected');
};
5. API文档
推荐使用OpenAPI (Swagger)规范:
openapi: 3.0.0
info:
title: Exchange API
version: 1.0.0
description: Cryptocurrency Exchange REST API
servers:
- url: https://api.example.com
description: Production server
paths:
/api/v1/markets:
get:
summary: Get all trading pairs
responses:
'200':
description: Successful response
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Market'
/api/v1/orders:
post:
summary: Create a new order
security:
- ApiKeyAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateOrderRequest'
responses:
'200':
description: Order created successfully
content:
application/json:
schema:
$ref: '#/components/schemas/Order'
components:
schemas:
Market:
type: object
properties:
symbol:
type: string
example: "BTCUSDT"
baseAsset:
type: string
example: "BTC"
quoteAsset:
type: string
example: "USDT"
CreateOrderRequest:
type: object
required:
- symbol
- side
- type
- quantity
properties:
symbol:
type: string
side:
type: string
enum: [buy, sell]
type:
type: string
enum: [limit, market]
price:
type: number
quantity:
type: number
Order:
type: object
properties:
orderId:
type: string
symbol:
type: string
side:
type: string
price:
type: number
quantity:
type: number
status:
type: string
securitySchemes:
ApiKeyAuth:
type: apiKey
in: header
name: X-API-Key
小结
交易所API设计的核心要点:
- RESTful API设计规范
- HMAC-SHA256签名认证
- WebSocket实时推送
- 完善的错误处理
- OpenAPI文档规范
下一章将讨论交易所的性能优化和高可用架构设计。