diff --git a/AssetManager.Infrastructure/Services/MarketDataService.cs b/AssetManager.Infrastructure/Services/MarketDataService.cs index 3b155de..6b50a81 100644 --- a/AssetManager.Infrastructure/Services/MarketDataService.cs +++ b/AssetManager.Infrastructure/Services/MarketDataService.cs @@ -39,7 +39,7 @@ public class MarketDataService : IMarketDataService } /// - /// 获取股票实时价格 + /// 获取股票实时价格(使用腾讯财经接口,免费无限制,支持盘前盘后) /// /// 股票代码 /// 股票价格信息 @@ -47,36 +47,81 @@ public class MarketDataService : IMarketDataService { try { - _logger.LogInformation($"Requesting stock price for symbol: {symbol}"); + _logger.LogInformation($"Requesting stock price for symbol: {symbol} (腾讯财经接口)"); - // Tiingo 日线最新价格端点(取最近1条) - var url = $"https://api.tiingo.com/tiingo/daily/{symbol}/prices?token={_tiingoApiKey}"; - var response = await _httpClient.GetFromJsonAsync>(url); + // 腾讯财经美股接口:前缀us,小写代码 + var url = $"http://qt.gtimg.cn/q=us{symbol.ToLower()}"; + var response = await _httpClient.GetStringAsync(url); - if (response == null || response.Count == 0) + if (string.IsNullOrEmpty(response) || !response.Contains("~")) { - throw new Exception($"No data found for {symbol}"); + throw new Exception($"腾讯财经接口返回无效数据,标的: {symbol}"); } - var latest = response[^1]; - decimal? prevClose = response.Count >= 2 ? response[^2].close : null; + // 解析返回数据 + var parts = response.Split('"')[1].Split('~'); + if (parts.Length < 36) + { + throw new Exception($"腾讯财经返回字段不足,标的: {symbol}"); + } + + // 提取字段:[3]=最新价 [4]=昨收价 + if (!decimal.TryParse(parts[3], out var currentPrice) || currentPrice <= 0) + { + throw new Exception($"解析最新价失败,标的: {symbol},返回值: {parts[3]}"); + } + + if (!decimal.TryParse(parts[4], out var prevClose) || prevClose <= 0) + { + prevClose = currentPrice; // 解析失败用当前价当昨收 + } return new MarketPriceResponse { Symbol = symbol, - Price = latest.close ?? 0, - PreviousClose = prevClose ?? 0, - Timestamp = latest.date ?? DateTime.UtcNow, + Price = currentPrice, + PreviousClose = prevClose, + Timestamp = DateTime.UtcNow, AssetType = "Stock" }; } catch (Exception ex) { - _logger.LogError(ex, $"Error getting stock price for {symbol}"); - throw; + _logger.LogError(ex, $"腾讯财经接口获取价格失败,标的: {symbol},降级使用Tiingo接口"); + // 降级使用Tiingo接口 + return await GetStockPriceFromTiingoAsync(symbol); } } + /// + /// 从Tiingo获取股票价格(降级用) + /// + private async Task GetStockPriceFromTiingoAsync(string symbol) + { + _logger.LogInformation($"Requesting stock price for symbol: {symbol} (Tiingo接口)"); + + // Tiingo 日线最新价格端点(取最近1条) + var url = $"https://api.tiingo.com/tiingo/daily/{symbol}/prices?token={_tiingoApiKey}"; + var response = await _httpClient.GetFromJsonAsync>(url); + + if (response == null || response.Count == 0) + { + throw new Exception($"No data found for {symbol}"); + } + + var latest = response[^1]; + decimal? prevClose = response.Count >= 2 ? response[^2].close : null; + + return new MarketPriceResponse + { + Symbol = symbol, + Price = latest.close ?? 0, + PreviousClose = prevClose ?? 0, + Timestamp = latest.date ?? DateTime.UtcNow, + AssetType = "Stock" + }; + } + /// /// 获取加密货币实时价格 ///