本文档详细介绍如何使用 PHP 语言对接 StockTV 印度股票数据源,覆盖 BSE(孟买证券交易所)和 NSE(印度国家证券交易所)的实时数据。
🚀 快速开始
环境要求
- PHP 7.4+
- cURL 扩展
- JSON 扩展
- 网络连接(可访问
api.stocktv.top)
🏗️ 核心架构
项目结构
src/
├── config/
│ └── StockTVConfig.php
├── models/
│ ├── Stock.php
│ ├── Index.php
│ ├── KLine.php
│ └── ApiResponse.php
├── clients/
│ ├── StockTVHttpClient.php
│ └── StockTVWebSocketClient.php
├── services/
│ └── IndiaStockService.php
└── examples/
└── IndiaStockDemo.php
📦 核心代码实现
1. 配置类
php
<?php
// src/config/StockTVConfig.php
namespace StockTV\Config;
/**
* StockTV API 配置类
*/
class StockTVConfig
{
// API 基础配置
const BASE_URL = 'https://api.stocktv.top';
const WS_URL = 'wss://ws-api.stocktv.top/connect';
// 印度市场配置
const INDIA_COUNTRY_ID = 14;
const NSE_EXCHANGE_ID = 46;
const BSE_EXCHANGE_ID = 74;
// API 接口路径
const STOCK_LIST = '/stock/stocks';
const QUERY_STOCKS = '/stock/queryStocks';
const STOCKS_BY_PIDS = '/stock/stocksByPids';
const INDICES = '/stock/indices';
const INDICES_BY_ID = '/stock/indicesById';
const KLINE = '/stock/kline';
const UPDOWN_LIST = '/stock/updownList';
const GET_IPO = '/stock/getIpo';
const COMPANIES = '/stock/companies';
const COMPANY_URL = '/stock/companyUrl';
const NEWS = '/stock/news';
private $apiKey;
private $timeout = 30;
public function __construct(string $apiKey)
{
$this->apiKey = $apiKey;
}
public function getApiKey(): string
{
return $this->apiKey;
}
public function getTimeout(): int
{
return $this->timeout;
}
public function setTimeout(int $timeout): self
{
$this->timeout = $timeout;
return $this;
}
}
2. 数据模型类
股票数据模型
php
<?php
// src/models/Stock.php
namespace StockTV\Models;
/**
* 印度股票数据模型
*/
class Stock
{
public $id;
public $symbol;
public $name;
public $last;
public $chg;
public $chgPct;
public $high;
public $low;
public $volume;
public $open;
public $exchangeId;
public $countryId;
public $countryNameTranslated;
public $flag;
public $fundamentalMarketCap;
public $fundamentalRevenue;
public $technicalDay;
public $technicalHour;
public $technicalWeek;
public $technicalMonth;
public $performanceDay;
public $performanceWeek;
public $performanceMonth;
public $performanceYtd;
public $time;
public $url;
public function __construct(array $data = [])
{
foreach ($data as $key => $value) {
if (property_exists($this, $key)) {
$this->$key = $value;
}
}
}
public function getExchangeName(): string
{
if ($this->exchangeId == \StockTV\Config\StockTVConfig::NSE_EXCHANGE_ID) {
return 'NSE';
} elseif ($this->exchangeId == \StockTV\Config\StockTVConfig::BSE_EXCHANGE_ID) {
return 'BSE';
}
return 'Unknown';
}
public function isGaining(): bool
{
return $this->chgPct > 0;
}
public function getFormattedChange(): string
{
$sign = $this->chgPct > 0 ? '+' : '';
return $sign . number_format($this->chgPct, 2) . '%';
}
public function getFormattedPrice(): string
{
return '₹' . number_format($this->last, 2);
}
}
指数数据模型
php
<?php
// src/models/Index.php
namespace StockTV\Models;
/**
* 指数数据模型
*/
class Index
{
public $id;
public $name;
public $symbol;
public $last;
public $chg;
public $chgPct;
public $high;
public $low;
public $isOpen;
public $flag;
public $url;
public $time;
public function __construct(array $data = [])
{
foreach ($data as $key => $value) {
if (property_exists($this, $key)) {
$this->$key = $value;
}
}
}
public function isGaining(): bool
{
return $this->chgPct > 0;
}
public function getFormattedChange(): string
{
$sign = $this->chgPct > 0 ? '+' : '';
return $sign . number_format($this->chgPct, 2) . '%';
}
}
K线数据模型
php
<?php
// src/models/KLine.php
namespace StockTV\Models;
/**
* K线数据模型
*/
class KLine
{
public $time;
public $open;
public $high;
public $low;
public $close;
public $volume;
public $vo;
public function __construct(array $data = [])
{
foreach ($data as $key => $value) {
if (property_exists($this, $key)) {
$this->$key = $value;
}
}
}
public function getAmplitude(): float
{
if ($this->open == 0) {
return 0;
}
return (($this->high - $this->low) / $this->open) * 100;
}
public function getChangePercent(): float
{
if ($this->open == 0) {
return 0;
}
return (($this->close - $this->open) / $this->open) * 100;
}
}
API响应包装类
php
<?php
// src/models/ApiResponse.php
namespace StockTV\Models;
/**
* API通用响应包装类
*/
class ApiResponse
{
public $code;
public $message;
public $data;
public function __construct(array $data = [])
{
foreach ($data as $key => $value) {
if (property_exists($this, $key)) {
$this->$key = $value;
}
}
}
public function isSuccess(): bool
{
return $this->code === 200;
}
}
/**
* 股票列表响应包装类
*/
class StockListResponse
{
public $records;
public $total;
public $size;
public $current;
public $pages;
public function __construct(array $data = [])
{
foreach ($data as $key => $value) {
if (property_exists($this, $key)) {
$this->$key = $value;
}
}
// 转换records为Stock对象数组
if (isset($data['records']) && is_array($data['records'])) {
$this->records = array_map(function($item) {
return new Stock($item);
}, $data['records']);
}
}
}
3. HTTP客户端实现
php
<?php
// src/clients/StockTVHttpClient.php
namespace StockTV\Clients;
use StockTV\Config\StockTVConfig;
use StockTV\Models\ApiResponse;
use StockTV\Models\Stock;
use StockTV\Models\Index;
use StockTV\Models\KLine;
use StockTV\Models\StockListResponse;
/**
* StockTV HTTP API客户端
*/
class StockTVHttpClient
{
private $config;
private $lastResponse;
public function __construct(StockTVConfig $config)
{
$this->config = $config;
}
/**
* 获取印度股票列表
*/
public function getIndiaStocks(int $pageSize = 50, int $page = 1): array
{
$params = [
'countryId' => StockTVConfig::INDIA_COUNTRY_ID,
'pageSize' => $pageSize,
'page' => $page,
'key' => $this->config->getApiKey()
];
$response = $this->makeRequest(StockTVConfig::STOCK_LIST, $params);
if ($response->isSuccess()) {
$stockListResponse = new StockListResponse($response->data);
return $stockListResponse->records;
}
throw new \Exception("获取印度股票列表失败: " . $response->message);
}
/**
* 查询单个股票
*/
public function queryStock(?int $id = null, ?string $symbol = null, ?string $name = null): array
{
$params = ['key' => $this->config->getApiKey()];
if ($id !== null) {
$params['id'] = $id;
}
if ($symbol !== null) {
$params['symbol'] = $symbol;
}
if ($name !== null) {
$params['name'] = $name;
}
$response = $this->makeRequest(StockTVConfig::QUERY_STOCKS, $params);
if ($response->isSuccess()) {
return array_map(function($item) {
return new Stock($item);
}, $response->data);
}
throw new \Exception("查询股票失败: " . $response->message);
}
/**
* 批量查询多个股票
*/
public function getStocksByPids(array $pids): array
{
if (empty($pids)) {
throw new \InvalidArgumentException("股票PID列表不能为空");
}
$params = [
'key' => $this->config->getApiKey(),
'pids' => implode(',', $pids)
];
$response = $this->makeRequest(StockTVConfig::STOCKS_BY_PIDS, $params);
if ($response->isSuccess()) {
return array_map(function($item) {
return new Stock($item);
}, $response->data);
}
throw new \Exception("批量查询股票失败: " . $response->message);
}
/**
* 获取印度主要指数
*/
public function getIndiaIndices(): array
{
$params = [
'countryId' => StockTVConfig::INDIA_COUNTRY_ID,
'key' => $this->config->getApiKey()
];
$response = $this->makeRequest(StockTVConfig::INDICES, $params);
if ($response->isSuccess()) {
return array_map(function($item) {
return new Index($item);
}, $response->data);
}
throw new \Exception("获取印度指数失败: " . $response->message);
}
/**
* 通过ID查询特定指数
*/
public function getIndexById(int $id): array
{
$params = [
'id' => $id,
'key' => $this->config->getApiKey()
];
$response = $this->makeRequest(StockTVConfig::INDICES_BY_ID, $params);
if ($response->isSuccess()) {
return array_map(function($item) {
return new Index($item);
}, $response->data);
}
throw new \Exception("获取指数失败: " . $response->message);
}
/**
* 获取K线数据
*/
public function getKLineData(int $pid, string $interval): array
{
$params = [
'pid' => $pid,
'interval' => $interval,
'key' => $this->config->getApiKey()
];
$response = $this->makeRequest(StockTVConfig::KLINE, $params);
if ($response->isSuccess()) {
return array_map(function($item) {
return new KLine($item);
}, $response->data);
}
throw new \Exception("获取K线数据失败: " . $response->message);
}
/**
* 获取涨跌排行榜
*/
public function getUpDownList(int $type): array
{
$params = [
'countryId' => StockTVConfig::INDIA_COUNTRY_ID,
'type' => $type,
'key' => $this->config->getApiKey()
];
$response = $this->makeRequest(StockTVConfig::UPDOWN_LIST, $params);
if ($response->isSuccess()) {
return array_map(function($item) {
return new Stock($item);
}, $response->data);
}
throw new \Exception("获取排行榜失败: " . $response->message);
}
/**
* 获取IPO数据
*/
public function getIpoList(?int $type = null): array
{
$params = [
'countryId' => StockTVConfig::INDIA_COUNTRY_ID,
'key' => $this->config->getApiKey()
];
if ($type !== null) {
$params['type'] = $type;
}
$response = $this->makeRequest(StockTVConfig::GET_IPO, $params);
if ($response->isSuccess()) {
return $response->data;
}
throw new \Exception("获取IPO数据失败: " . $response->message);
}
/**
* 通用HTTP请求方法
*/
private function makeRequest(string $endpoint, array $params = []): ApiResponse
{
$url = StockTVConfig::BASE_URL . $endpoint . '?' . http_build_query($params);
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => $this->config->getTimeout(),
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'User-Agent: StockTV-PHP-Client/1.0'
]
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
if ($error) {
throw new \Exception("HTTP请求失败: " . $error);
}
if ($httpCode !== 200) {
throw new \Exception("HTTP请求失败,状态码: " . $httpCode);
}
$data = json_decode($response, true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new \Exception("JSON解析失败: " . json_last_error_msg());
}
$this->lastResponse = $data;
return new ApiResponse($data);
}
/**
* 获取最后一次响应数据
*/
public function getLastResponse(): ?array
{
return $this->lastResponse;
}
}
4. WebSocket客户端实现
php
<?php
// src/clients/StockTVWebSocketClient.php
namespace StockTV\Clients;
use StockTV\Config\StockTVConfig;
use Ratchet\Client\WebSocket;
use Ratchet\Client\Connector;
use React\EventLoop\Factory;
use React\Socket\Connector as ReactConnector;
/**
* StockTV WebSocket实时数据客户端
*/
class StockTVWebSocketClient
{
private $config;
private $loop;
private $connector;
private $webSocket;
private $callbacks;
public function __construct(StockTVConfig $config)
{
$this->config = $config;
$this->loop = Factory::create();
$this->connector = new Connector($this->loop);
$this->callbacks = [
'message' => [],
'error' => [],
'close' => []
];
}
/**
* 连接WebSocket服务器
*/
public function connect(): void
{
$wsUrl = StockTVConfig::WS_URL . '?key=' . $this->config->getApiKey();
$this->connector->__invoke($wsUrl)
->then(function(WebSocket $conn) {
$this->webSocket = $conn;
$this->onOpen($conn);
$conn->on('message', function($msg) use ($conn) {
$this->onMessage($conn, $msg);
});
$conn->on('close', function($code = null, $reason = null) use ($conn) {
$this->onClose($conn, $code, $reason);
});
}, function(\Exception $e) {
$this->onError($e);
});
}
/**
* 启动事件循环
*/
public function run(): void
{
$this->loop->run();
}
/**
* 停止事件循环
*/
public function stop(): void
{
if ($this->loop) {
$this->loop->stop();
}
}
/**
* 连接建立回调
*/
private function onOpen(WebSocket $conn): void
{
echo "WebSocket连接已建立\n";
}
/**
* 消息接收回调
*/
private function onMessage(WebSocket $conn, $msg): void
{
$data = json_decode($msg, true);
if (json_last_error() !== JSON_ERROR_NONE) {
echo "JSON解析失败: " . json_last_error_msg() . "\n";
return;
}
$this->handleRealTimeData($data);
// 执行用户定义的回调
foreach ($this->callbacks['message'] as $callback) {
call_user_func($callback, $data);
}
}
/**
* 连接关闭回调
*/
private function onClose(WebSocket $conn, $code, $reason): void
{
echo "WebSocket连接已关闭: code={$code}, reason={$reason}\n";
foreach ($this->callbacks['close'] as $callback) {
call_user_func($callback, $code, $reason);
}
}
/**
* 错误回调
*/
private function onError(\Exception $e): void
{
echo "WebSocket连接错误: " . $e->getMessage() . "\n";
foreach ($this->callbacks['error'] as $callback) {
call_user_func($callback, $e);
}
}
/**
* 处理实时数据
*/
private function handleRealTimeData(array $data): void
{
if (isset($data['pid'])) {
$symbol = $data['symbol'] ?? $data['pid'];
$price = $data['last_numeric'] ?? 'N/A';
$changePercent = $data['pcp'] ?? '0';
echo "实时行情: {$symbol} - 价格: {$price}, 涨跌幅: {$changePercent}%\n";
// 价格预警逻辑
$changePercentNum = floatval($changePercent);
if (abs($changePercentNum) > 2.0) {
echo "🚨 价格波动预警: {$symbol} 波动 {$changePercentNum}%\n";
}
}
}
/**
* 添加消息回调
*/
public function onMessageCallback(callable $callback): self
{
$this->callbacks['message'][] = $callback;
return $this;
}
/**
* 添加错误回调
*/
public function onErrorCallback(callable $callback): self
{
$this->callbacks['error'][] = $callback;
return $this;
}
/**
* 添加关闭回调
*/
public function onCloseCallback(callable $callback): self
{
$this->callbacks['close'][] = $callback;
return $this;
}
/**
* 发送消息
*/
public function send(string $message): void
{
if ($this->webSocket) {
$this->webSocket->send($message);
}
}
/**
* 关闭连接
*/
public function close(): void
{
if ($this->webSocket) {
$this->webSocket->close();
}
$this->stop();
}
}
5. 服务层封装
php
<?php
// src/services/IndiaStockService.php
namespace StockTV\Services;
use StockTV\Config\StockTVConfig;
use StockTV\Clients\StockTVHttpClient;
use StockTV\Clients\StockTVWebSocketClient;
use StockTV\Models\Stock;
use StockTV\Models\Index;
use StockTV\Models\KLine;
/**
* 印度股票数据服务
*/
class IndiaStockService
{
private $httpClient;
private $wsClient;
public function __construct(string $apiKey)
{
$config = new StockTVConfig($apiKey);
$this->httpClient = new StockTVHttpClient($config);
$this->wsClient = new StockTVWebSocketClient($config);
}
/**
* 获取Nifty 50成分股
*/
public function getNifty50Stocks(): array
{
try {
$stocks = $this->httpClient->getIndiaStocks(50, 1);
echo "成功获取 " . count($stocks) . " 只印度股票\n";
return $stocks;
} catch (\Exception $e) {
echo "获取Nifty 50成分股失败: " . $e->getMessage() . "\n";
throw $e;
}
}
/**
* 获取印度主要指数
*/
public function getMajorIndices(): array
{
try {
$indices = $this->httpClient->getIndiaIndices();
echo "成功获取 " . count($indices) . " 个印度指数\n";
return $indices;
} catch (\Exception $e) {
echo "获取印度指数失败: " . $e->getMessage() . "\n";
throw $e;
}
}
/**
* 查询特定股票
*/
public function getStockBySymbol(string $symbol): ?Stock
{
try {
$stocks = $this->httpClient->queryStock(null, $symbol, null);
if (empty($stocks)) {
echo "未找到股票: {$symbol}\n";
return null;
}
echo "查询股票 {$symbol} 成功\n";
return $stocks[0];
} catch (\Exception $e) {
echo "查询股票失败: {$symbol} - " . $e->getMessage() . "\n";
throw $e;
}
}
/**
* 批量查询股票
*/
public function getStocksBySymbols(array $symbols): array
{
$results = [];
foreach ($symbols as $symbol) {
try {
$stock = $this->getStockBySymbol($symbol);
if ($stock) {
$results[] = $stock;
}
} catch (\Exception $e) {
// 单个股票查询失败,继续处理其他股票
continue;
}
}
echo "批量查询成功,获取 " . count($results) . " 只股票\n";
return $results;
}
/**
* 获取股票K线数据
*/
public function getStockKLine(int $pid, string $interval): array
{
try {
$klines = $this->httpClient->getKLineData($pid, $interval);
echo "成功获取股票 {$pid} 的K线数据,共 " . count($klines) . " 条\n";
return $klines;
} catch (\Exception $e) {
echo "获取K线数据失败: pid={$pid} - " . $e->getMessage() . "\n";
throw $e;
}
}
/**
* 获取涨幅榜
*/
public function getGainers(): array
{
try {
$gainers = $this->httpClient->getUpDownList(1);
echo "成功获取涨幅榜,共 " . count($gainers) . " 只股票\n";
return $gainers;
} catch (\Exception $e) {
echo "获取涨幅榜失败: " . $e->getMessage() . "\n";
throw $e;
}
}
/**
* 获取跌幅榜
*/
public function getLosers(): array
{
try {
$losers = $this->httpClient->getUpDownList(2);
echo "成功获取跌幅榜,共 " . count($losers) . " 只股票\n";
return $losers;
} catch (\Exception $e) {
echo "获取跌幅榜失败: " . $e->getMessage() . "\n";
throw $e;
}
}
/**
* 获取IPO数据
*/
public function getUpcomingIPOs(): array
{
try {
$ipos = $this->httpClient->getIpoList(1); // 1表示未上市
echo "成功获取IPO数据,共 " . count($ipos) . " 个\n";
return $ipos;
} catch (\Exception $e) {
echo "获取IPO数据失败: " . $e->getMessage() . "\n";
throw $e;
}
}
/**
* 启动实时数据监控
*/
public function startRealTimeMonitoring(): void
{
try {
// 添加消息处理回调
$this->wsClient->onMessageCallback(function($data) {
$this->handleRealTimeData($data);
});
$this->wsClient->connect();
echo "实时数据监控已启动\n";
// 启动事件循环
$this->wsClient->run();
} catch (\Exception $e) {
echo "启动实时数据监控失败: " . $e->getMessage() . "\n";
throw $e;
}
}
/**
* 处理实时数据
*/
private function handleRealTimeData(array $data): void
{
if (isset($data['pid'])) {
$symbol = $data['symbol'] ?? $data['pid'];
$price = $data['last_numeric'] ?? 'N/A';
$changePercent = $data['pcp'] ?? '0';
$trend = floatval($changePercent) >= 0 ? '📈' : '📉';
echo "{$trend} 实时行情: {$symbol} - 价格: ₹{$price}, 涨跌幅: {$changePercent}%\n";
// 价格预警逻辑
$changePercentNum = floatval($changePercent);
if (abs($changePercentNum) > 5.0) {
echo "🚨 大幅波动预警: {$symbol} 波动 {$changePercentNum}%\n";
}
}
}
/**
* 停止实时数据监控
*/
public function stopRealTimeMonitoring(): void
{
$this->wsClient->close();
echo "实时数据监控已停止\n";
}
}
6. 使用示例
php
<?php
// examples/IndiaStockDemo.php
require_once __DIR__ . '/../vendor/autoload.php';
use StockTV\Services\IndiaStockService;
use StockTV\Models\Stock;
use StockTV\Models\Index;
/**
* 印度股票数据使用示例
*/
class IndiaStockDemo
{
private $stockService;
public function __construct(string $apiKey)
{
$this->stockService = new IndiaStockService($apiKey);
}
public function runDemo(): void
{
echo "=== StockTV 印度股票数据演示程序开始 ===\n\n";
try {
// 1. 获取印度主要指数
$this->demonstrateIndices();
// 2. 查询特定股票
$this->demonstrateStockQuery();
// 3. 获取Nifty 50成分股示例
$this->demonstrateNifty50();
// 4. 获取K线数据
$this->demonstrateKLineData();
// 5. 获取排行榜
$this->demonstrateRankings();
echo "\n=== 演示程序执行完成 ===\n";
} catch (Exception $e) {
echo "演示程序执行失败: " . $e->getMessage() . "\n";
}
}
/**
* 演示指数数据获取
*/
private function demonstrateIndices(): void
{
echo "1. 印度主要指数\n";
echo str_repeat("-", 50) . "\n";
$indices = $this->stockService->getMajorIndices();
foreach ($indices as $index) {
$trend = $index->isGaining() ? '📈' : '📉';
$changeSign = $index->isGaining() ? '+' : '';
echo "{$trend} {$index->name}: {$index->last} ";
echo "({$changeSign}{$index->chgPct}%)\n";
}
echo "\n";
}
/**
* 演示股票查询
*/
private function demonstrateStockQuery(): void
{
echo "2. 查询特定股票\n";
echo str_repeat("-", 50) . "\n";
// 查询Reliance Industries
$reliance = $this->stockService->getStockBySymbol('RELIANCE');
$this->printStockInfo($reliance, 'Reliance Industries');
// 查询TCS
$tcs = $this->stockService->getStockBySymbol('TCS');
$this->printStockInfo($tcs, 'Tata Consultancy Services');
echo "\n";
}
/**
* 演示Nifty 50成分股
*/
private function demonstrateNifty50(): void
{
echo "3. Nifty 50成分股(示例)\n";
echo str_repeat("-", 50) . "\n";
$niftyStocks = $this->stockService->getNifty50Stocks();
// 显示前10只股票
$count = 0;
foreach ($niftyStocks as $stock) {
if ($count >= 10) break;
$trend = $stock->isGaining() ? '🟢' : '🔴';
$changeSign = $stock->isGaining() ? '+' : '';
echo "{$trend} {$stock->symbol}: ₹{$stock->last} ";
echo "({$changeSign}{$stock->chgPct}%) - {$stock->name}\n";
$count++;
}
echo "\n";
}
/**
* 演示K线数据获取
*/
private function demonstrateKLineData(): void
{
echo "4. K线数据示例\n";
echo str_repeat("-", 50) . "\n";
$reliance = $this->stockService->getStockBySymbol('RELIANCE');
if ($reliance) {
$klines = $this->stockService->getStockKLine($reliance->id, 'P1D');
echo "Reliance Industries 近期日K线数据:\n";
$count = 0;
foreach ($klines as $kline) {
if ($count >= 5) break;
$date = date('Y-m-d H:i:s', $kline->time / 1000);
$amplitude = number_format($kline->getAmplitude(), 2);
echo "时间: {$date}, 开: ₹{$kline->open}, ";
echo "高: ₹{$kline->high}, 低: ₹{$kline->low}, ";
echo "收: ₹{$kline->close}, 振幅: {$amplitude}%\n";
$count++;
}
}
echo "\n";
}
/**
* 演示排行榜功能
*/
private function demonstrateRankings(): void
{
echo "5. 市场排行榜\n";
echo str_repeat("-", 50) . "\n";
// 获取涨幅榜
$gainers = $this->stockService->getGainers();
echo "📈 今日涨幅榜(前5):\n";
$count = 0;
foreach ($gainers as $stock) {
if ($count >= 5) break;
$changeSign = $stock->isGaining() ? '+' : '';
echo " {$stock->symbol}: ₹{$stock->last} ";
echo "({$changeSign}{$stock->chgPct}%) - {$stock->name}\n";
$count++;
}
echo "\n";
// 获取跌幅榜
$losers = $this->stockService->getLosers();
echo "📉 今日跌幅榜(前5):\n";
$count = 0;
foreach ($losers as $stock) {
if ($count >= 5) break;
echo " {$stock->symbol}: ₹{$stock->last} ";
echo "({$stock->chgPct}%) - {$stock->name}\n";
$count++;
}
echo "\n";
}
/**
* 打印股票信息
*/
private function printStockInfo(?Stock $stock, string $description): void
{
if ($stock) {
$status = $stock->open ? '🟢 交易中' : '🔴 已收盘';
$trend = $stock->isGaining() ? '📈' : '📉';
echo "{$trend} {$description} - {$status}\n";
echo " 代码: {$stock->symbol} | 价格: ₹{$stock->last}\n";
echo " 涨跌: ₹{$stock->chg} ({$stock->getFormattedChange()})\n";
echo " 最高: ₹{$stock->high} | 最低: ₹{$stock->low} | 成交量: {$stock->volume}\n";
if ($stock->technicalDay) {
$techName = $this->getTechnicalIndicatorName($stock->technicalDay);
echo " 技术指标: {$techName}\n";
}
}
echo "\n";
}
/**
* 获取技术指标中文名称
*/
private function getTechnicalIndicatorName(string $indicator): string
{
$map = [
'strong_buy' => '强烈买入',
'buy' => '买入',
'neutral' => '中性',
'sell' => '卖出',
'strong_sell' => '强烈卖出'
];
return $map[$indicator] ?? $indicator;
}
}
// 运行演示程序
$apiKey = '您的API_KEY'; // 替换为实际的API Key
$demo = new IndiaStockDemo($apiKey);
$demo->runDemo();
🎯 高级功能
实时价格监控器
php
<?php
// examples/PriceMonitor.php
require_once __DIR__ . '/../vendor/