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)
};
}
}