feat: 接入 OKX 加密货币数据源(实时价格+历史K线)

This commit is contained in:
虾球 2026-03-07 03:08:46 +00:00
parent 8830dd17ae
commit 20ab0c5173

View File

@ -70,12 +70,38 @@ public class MarketDataService : IMarketDataService
/// <summary> /// <summary>
/// 获取加密货币实时价格 /// 获取加密货币实时价格
/// </summary> /// </summary>
/// <param name="symbol">加密货币代码(如 BTCUSD</param> /// <param name="symbol">加密货币代码(如 BTC-USDT</param>
/// <returns>加密货币价格信息</returns> /// <returns>加密货币价格信息</returns>
public async Task<MarketPriceResponse> GetCryptoPriceAsync(string symbol) public async Task<MarketPriceResponse> GetCryptoPriceAsync(string symbol)
{ {
// 待接入 OKX API try
throw new NotImplementedException("加密货币数据源待接入 OKX API"); {
_logger.LogInformation($"Requesting crypto price for symbol: {symbol}");
// OKX 实时 ticker 接口symbol 格式如 BTC-USDT
var url = $"https://www.okx.com/api/v5/market/ticker?instId={symbol}";
var response = await _httpClient.GetFromJsonAsync<OkxTickerResponse>(url);
if (response == null || response.code != "0" || response.data == null || response.data.Count == 0)
{
throw new Exception($"No data found for {symbol}, code: {response?.code}, msg: {response?.msg}");
}
var latest = response.data[0];
return new MarketPriceResponse
{
Symbol = symbol,
Price = decimal.TryParse(latest.last, out var price) ? price : 0,
PreviousClose = decimal.TryParse(latest.sodUtc0, out var prevClose) ? prevClose : 0, // UTC0 开盘价作为昨日收盘价
Timestamp = DateTime.UtcNow,
AssetType = "Crypto"
};
}
catch (Exception ex)
{
_logger.LogError(ex, $"Error getting crypto price for {symbol}");
throw;
}
} }
/// <summary> /// <summary>
@ -133,14 +159,82 @@ public class MarketDataService : IMarketDataService
/// <summary> /// <summary>
/// 获取加密货币历史数据 /// 获取加密货币历史数据
/// </summary> /// </summary>
/// <param name="symbol">加密货币代码</param> /// <param name="symbol">加密货币代码(如 BTC-USDT</param>
/// <param name="timeframe">时间周期</param> /// <param name="timeframe">时间周期</param>
/// <param name="limit">数据点数量</param> /// <param name="limit">数据点数量</param>
/// <returns>历史数据列表</returns> /// <returns>历史数据列表</returns>
public async Task<List<MarketDataResponse>> GetCryptoHistoricalDataAsync(string symbol, string timeframe, int limit) public async Task<List<MarketDataResponse>> GetCryptoHistoricalDataAsync(string symbol, string timeframe, int limit)
{ {
// 待接入 OKX API try
throw new NotImplementedException("加密货币数据源待接入 OKX API"); {
_logger.LogInformation($"Requesting crypto historical data for symbol: {symbol}, timeframe: {timeframe}, limit: {limit}");
// 转换 timeframe 为 OKX 支持的粒度
var bar = ConvertToOkxBar(timeframe);
// OKX K线接口
var url = $"https://www.okx.com/api/v5/market/candles?instId={symbol}&bar={bar}&limit={limit}";
var response = await _httpClient.GetFromJsonAsync<OkxCandlesResponse>(url);
if (response == null || response.code != "0" || response.data == null)
{
throw new Exception($"No data found for {symbol}, code: {response?.code}, msg: {response?.msg}");
}
var result = new List<MarketDataResponse>();
foreach (var item in response.data)
{
if (item.Length < 6) continue;
if (long.TryParse(item[0], out var ts) &&
decimal.TryParse(item[1], out var open) &&
decimal.TryParse(item[2], out var high) &&
decimal.TryParse(item[3], out var low) &&
decimal.TryParse(item[4], out var close) &&
decimal.TryParse(item[5], out var volume))
{
result.Add(new MarketDataResponse
{
Symbol = symbol,
Open = open,
High = high,
Low = low,
Close = close,
Volume = volume,
Timestamp = DateTimeOffset.FromUnixTimeMilliseconds(ts).UtcDateTime,
AssetType = "Crypto"
});
}
}
// 按时间升序排列
result = result.OrderBy(x => x.Timestamp).ToList();
return result;
}
catch (Exception ex)
{
_logger.LogError(ex, $"Error getting crypto historical data for {symbol}");
throw;
}
}
/// <summary>
/// 转换 timeframe 为 OKX 支持的 bar 格式
/// </summary>
private string ConvertToOkxBar(string timeframe)
{
return timeframe.ToLower() switch
{
"1min" => "1m",
"5min" => "5m",
"15min" => "15m",
"1h" => "1H",
"4h" => "4H",
"1d" => "1D",
"1w" => "1W",
"1m" => "1M",
_ => "1D"
};
} }
/// <summary> /// <summary>
@ -195,3 +289,25 @@ internal class TiingoCryptoBar
public decimal? volume { get; set; } public decimal? volume { get; set; }
public DateTime? date { get; set; } public DateTime? date { get; set; }
} }
// OKX 响应模型
internal class OkxTickerResponse
{
public string code { get; set; }
public string msg { get; set; }
public List<OkxTickerData> data { get; set; }
}
internal class OkxTickerData
{
public string instId { get; set; }
public string last { get; set; }
public string sodUtc0 { get; set; } // UTC0 开盘价,作为昨日收盘价
}
internal class OkxCandlesResponse
{
public string code { get; set; }
public string msg { get; set; }
public List<string[]> data { get; set; }
}