PHP实战:手把手教你对接外汇,黄金等行情数据api接口

5 阅读3分钟

​ 最近在做一个金融相关的项目,需要对接股票,外汇和黄金的行情数据。在网上找了一圈,发现了一个还算稳定的数据源。“脉动行情数据” 今天就把对接过程中用到的“获取实时数据接口”这部分整理出来,分享给大家。

先说重点

这个接口是HTTP的GET请求,比较简单。不过有几个地方需要注意:

  1. 频率限制:每个产品每秒可请求3次,完全够用了
  2. 压缩传输:建议启用gzip压缩,能省不少流量

接口基本信息

  • 地址http://39.107.99.235:1008/getQuote.php
  • 方式:GET
  • 参数:就一个code,传产品代码就行
  • 返回:JSON格式

上代码,直接开干

我习惯先写个工具类,后面用起来方便:

<?php
/**
 * 行情数据接口封装类
 * 作者:我自己
 * 日期:2024年1月
 */
class MarketApi {

    // 数据演示站
    private $apiUrl = 'http://39.107.99.235:1008/market';

    // 接口地址
    private $apiUrl = 'http://39.107.99.235:1008/getQuote.php';
    
    // 上次请求时间记录(用于控制频率)
    private static $lastRequestTime = [];
    
    /**
     * 获取单个产品的行情数据
     * @param string $code 产品代码,比如 btcusdt, ethusdt
     * @return array
     */
    public function getQuote($code) {
        // 检查请求频率
        if (!$this->checkRateLimit($code)) {
            return [
                'success' => false,
                'message' => '请求太频繁了,请稍后再试'
            ];
        }
        
        // 构建请求URL
        $url = $this->apiUrl . '?code=' . urlencode($code);
        
        // 设置请求头,启用gzip压缩
        $headers = [
            'Accept-Encoding: gzip',
            'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
        ];
        
        // 发起请求
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_TIMEOUT, 10);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        
        if (curl_errno($ch)) {
            $error = curl_error($ch);
            curl_close($ch);
            return [
                'success' => false,
                'message' => '请求失败:' . $error
            ];
        }
        
        curl_close($ch);
        
        // 处理gzip压缩的响应
        if (substr($response, 0, 2) == "\x1f\x8b") {
            $response = gzdecode($response);
        }
        
        // 解析响应数据
        $result = json_decode($response, true);
        
        if ($httpCode != 200) {
            return [
                'success' => false,
                'message' => '接口返回错误,HTTP码:' . $httpCode,
                'data' => $result
            ];
        }
        
        if (json_last_error() !== JSON_ERROR_NONE) {
            return [
                'success' => false,
                'message' => 'JSON解析失败:' . json_last_error_msg()
            ];
        }
        
        // 记录本次请求时间
        $this->recordRequestTime($code);
        
        return [
            'success' => true,
            'data' => $result
        ];
    }
    
    /**
     * 批量获取多个产品行情
     * @param array $codes 产品代码数组
     * @return array
     */
    public function getBatchQuotes($codes) {
        $results = [];
        
        foreach ($codes as $code) {
            // 每个请求之间稍微停顿一下,避免触发限流
            usleep(100000); // 0.1秒
            
            $result = $this->getQuote($code);
            $results[$code] = $result;
        }
        
        return $results;
    }
    
    /**
     * 检查请求频率
     * @param string $code 产品代码
     * @return bool
     */
    private function checkRateLimit($code) {
        if (!isset(self::$lastRequestTime[$code])) {
            return true;
        }
        
        $lastTime = self::$lastRequestTime[$code];
        $currentTime = microtime(true);
        $interval = $currentTime - $lastTime;
        
        // 确保两次请求间隔至少0.34秒(略高于1/3秒)
        return $interval >= 0.34;
    }
    
    /**
     * 记录请求时间
     * @param string $code 产品代码
     */
    private function recordRequestTime($code) {
        self::$lastRequestTime[$code] = microtime(true);
    }
    
    /**
     * 提取主要行情信息(简化版)
     * @param array $apiData 接口返回的原始数据
     * @return array
     */
    public function extractMainData($apiData) {
        if (empty($apiData['data']['body'])) {
            return null;
        }
        
        $body = $apiData['data']['body'];
        
        return [
            'code' => $body['StockCode'] ?? '',
            'latest_price' => $body['Price'] ?? 0,
            'open_price' => $body['Open'] ?? 0,
            'high_price' => $body['High'] ?? 0,
            'low_price' => $body['Low'] ?? 0,
            'volume' => $body['TotalVol'] ?? 0,
            'change' => $body['Diff'] ?? 0,
            'change_rate' => $body['DiffRate'] ?? 0,
            'update_time' => $body['Time'] ?? '',
            'buy_price' => $body['BP1'] ?? 0,
            'buy_volume' => $body['BV1'] ?? 0,
            'sell_price' => $body['SP1'] ?? 0,
            'sell_volume' => $body['SV1'] ?? 0
        ];
    }
}

使用示例

<?php
// 引入上面的类
require_once 'MarketApi.php';

// 创建实例
$marketApi = new MarketApi();

// 示例1:获取单个产品行情
echo "=== 获取BTC行情 ===\n";
$btcResult = $marketApi->getQuote('btcusdt');

if ($btcResult['success']) {
    $mainData = $marketApi->extractMainData($btcResult['data']);
    
    echo "产品代码:{$mainData['code']}\n";
    echo "最新价格:{$mainData['latest_price']}\n";
    echo "涨跌幅:{$mainData['change_rate']}%\n";
    echo "更新时间:{$mainData['update_time']}\n";
    echo "买一价:{$mainData['buy_price']}\n";
    echo "卖一价:{$mainData['sell_price']}\n";
    echo "成交量:{$mainData['volume']}\n";
} else {
    echo "获取失败:{$btcResult['message']}\n";
}

// 示例2:批量获取
echo "\n=== 批量获取多个产品 ===\n";
$codes = ['ethusdt', 'bchusdt', 'xrpusdt'];
$batchResults = $marketApi->getBatchQuotes($codes);

foreach ($batchResults as $code => $result) {
    if ($result['success']) {
        $data = $marketApi->extractMainData($result['data']);
        echo "{$code}: {$data['latest_price']} ({$data['change_rate']}%)\n";
    } else {
        echo "{$code}: 获取失败\n";
    }
}

// 示例3:查看完整的接口返回数据
echo "\n=== 查看完整数据结构 ===\n";
$fullResult = $marketApi->getQuote('ethusdt');
if ($fullResult['success']) {
    // 这里可以打印完整数据或者保存到数据库
    // print_r($fullResult['data']);
    echo "获取成功,数据已收到\n";
}

实际开发中的经验

1. 数据库存储

如果要做行情分析,建议存数据库,如果不用那就算了:

class MarketDataSaver {
    private $db;
    
    public function __construct($db) {
        $this->db = $db;
    }
    
    public function saveQuoteData($code, $quoteData) {
        $mainData = $quoteData['data']['body'] ?? [];
        
        if (empty($mainData)) {
            return false;
        }
        
        $sql = "INSERT INTO market_quotes 
                (stock_code, price, open_price, high_price, low_price, 
                 volume, change_amount, change_rate, update_time) 
                VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
        
        $stmt = $this->db->prepare($sql);
        return $stmt->execute([
            $mainData['StockCode'],
            $mainData['Price'],
            $mainData['Open'],
            $mainData['High'],
            $mainData['Low'],
            $mainData['TotalVol'],
            $mainData['Diff'],
            $mainData['DiffRate'],
            $mainData['Time']
        ]);
    }
}

总结

这个接口用起来还算简单,主要是注意频率控制和错误处理。如果要更高频率的数据,建议用他们的WebSocket接口。

上面的代码是我实际项目中提炼出来的,可以直接用。如果有问题,欢迎在评论区讨论。

注意:这个接口需要授权才能用,别直接拿代码去跑,先联系客服加白名单。

希望对大家有帮助!