using Alpaca.Markets; using AssetManager.Models.DTOs; using Microsoft.Extensions.Logging; namespace AssetManager.Infrastructure.Services; /// /// 市场数据服务实现 /// public class MarketDataService : IMarketDataService { private readonly ILogger _logger; private readonly IAlpacaDataClient _dataClient; /// /// 构造函数 /// /// 日志记录器 public MarketDataService(ILogger logger) { _logger = logger; // 初始化 Alpaca 客户端 (7.2 版本) var secretKey = new SecretKey("YOUR_API_KEY", "YOUR_SECRET_KEY"); // 使用 Paper Trading 环境进行测试,生产环境请使用 Environments.Live _dataClient = Environments.Paper.GetAlpacaDataClient(secretKey); } /// /// 获取股票实时价格 /// /// 股票代码 /// 股票价格信息 public async Task GetStockPriceAsync(string symbol) { try { _logger.LogInformation($"Requesting stock price for symbol: {symbol}"); var request = new LatestMarketDataRequest(symbol); var latestTrade = await _dataClient.GetLatestTradeAsync(request); return new MarketPriceResponse { Symbol = symbol, Price = latestTrade.Price, Timestamp = latestTrade.TimestampUtc, AssetType = "Stock" }; } catch (Exception ex) { _logger.LogError(ex, $"Error getting stock price for {symbol}"); throw; } } /// /// 获取加密货币实时价格 /// /// 加密货币代码 /// 加密货币价格信息 public async Task GetCryptoPriceAsync(string symbol) { try { _logger.LogInformation($"Requesting crypto price for symbol: {symbol}"); var request = new LatestMarketDataRequest(symbol); var latestTrade = await _dataClient.GetLatestTradeAsync(request); return new MarketPriceResponse { Symbol = symbol, Price = latestTrade.Price, Timestamp = latestTrade.TimestampUtc, AssetType = "Crypto" }; } catch (Exception ex) { _logger.LogError(ex, $"Error getting crypto price for {symbol}"); throw; } } /// /// 获取股票历史数据 /// /// 股票代码 /// 时间周期 /// 数据点数量 /// 历史数据列表 public async Task> GetStockHistoricalDataAsync(string symbol, string timeframe, int limit) { try { _logger.LogInformation($"Requesting stock historical data for symbol: {symbol}, timeframe: {timeframe}, limit: {limit}"); var barTimeFrame = GetBarTimeFrame(timeframe); var endDate = DateTime.UtcNow; var startDate = CalculateStartDate(endDate, timeframe, limit); var request = new HistoricalBarsRequest(symbol, startDate, endDate, barTimeFrame); var barsPage = await _dataClient.GetHistoricalBarsAsync(request); var result = new List(); foreach (var kvp in barsPage.Items) { foreach (var bar in kvp.Value) { result.Add(new MarketDataResponse { Symbol = symbol, Open = bar.Open, High = bar.High, Low = bar.Low, Close = bar.Close, Volume = bar.Volume, Timestamp = bar.TimeUtc, AssetType = "Stock" }); } } return result; } catch (Exception ex) { _logger.LogError(ex, $"Error getting stock historical data for {symbol}"); throw; } } /// /// 获取加密货币历史数据 /// /// 加密货币代码 /// 时间周期 /// 数据点数量 /// 历史数据列表 public async Task> GetCryptoHistoricalDataAsync(string symbol, string timeframe, int limit) { try { _logger.LogInformation($"Requesting crypto historical data for symbol: {symbol}, timeframe: {timeframe}, limit: {limit}"); var barTimeFrame = GetBarTimeFrame(timeframe); var endDate = DateTime.UtcNow; var startDate = CalculateStartDate(endDate, timeframe, limit); var request = new HistoricalBarsRequest(symbol, startDate, endDate, barTimeFrame); var barsPage = await _dataClient.GetHistoricalBarsAsync(request); var result = new List(); foreach (var kvp in barsPage.Items) { foreach (var bar in kvp.Value) { result.Add(new MarketDataResponse { Symbol = symbol, Open = bar.Open, High = bar.High, Low = bar.Low, Close = bar.Close, Volume = bar.Volume, Timestamp = bar.TimeUtc, AssetType = "Crypto" }); } } return result; } catch (Exception ex) { _logger.LogError(ex, $"Error getting crypto historical data for {symbol}"); throw; } } /// /// 转换时间周期 /// /// 时间周期字符串 /// BarTimeFrame 对象 private BarTimeFrame GetBarTimeFrame(string timeframe) { return timeframe.ToLower() switch { "1min" => BarTimeFrame.Minute, "5min" => BarTimeFrame.Minute, "15min" => BarTimeFrame.Minute, "1h" => BarTimeFrame.Hour, "1d" => BarTimeFrame.Day, "1w" => BarTimeFrame.Week, "1m" => BarTimeFrame.Month, _ => BarTimeFrame.Day }; } /// /// 计算开始日期 /// /// 结束日期 /// 时间周期 /// 数据点数量 /// 开始日期 private DateTime CalculateStartDate(DateTime endDate, string timeframe, int limit) { return timeframe.ToLower() switch { "1min" => endDate.AddMinutes(-limit), "5min" => endDate.AddMinutes(-limit * 5), "15min" => endDate.AddMinutes(-limit * 15), "1h" => endDate.AddHours(-limit), "1d" => endDate.AddDays(-limit), "1w" => endDate.AddDays(-limit * 7), "1m" => endDate.AddMonths(-limit), _ => endDate.AddDays(-limit) }; } }