feat: 接入 OKX 加密货币数据源(实时价格+历史K线)
This commit is contained in:
parent
8830dd17ae
commit
20ab0c5173
@ -70,12 +70,38 @@ public class MarketDataService : IMarketDataService
|
||||
/// <summary>
|
||||
/// 获取加密货币实时价格
|
||||
/// </summary>
|
||||
/// <param name="symbol">加密货币代码(如 BTCUSD)</param>
|
||||
/// <param name="symbol">加密货币代码(如 BTC-USDT)</param>
|
||||
/// <returns>加密货币价格信息</returns>
|
||||
public async Task<MarketPriceResponse> GetCryptoPriceAsync(string symbol)
|
||||
{
|
||||
// 待接入 OKX API
|
||||
throw new NotImplementedException("加密货币数据源待接入 OKX API");
|
||||
try
|
||||
{
|
||||
_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>
|
||||
@ -133,14 +159,82 @@ public class MarketDataService : IMarketDataService
|
||||
/// <summary>
|
||||
/// 获取加密货币历史数据
|
||||
/// </summary>
|
||||
/// <param name="symbol">加密货币代码</param>
|
||||
/// <param name="symbol">加密货币代码(如 BTC-USDT)</param>
|
||||
/// <param name="timeframe">时间周期</param>
|
||||
/// <param name="limit">数据点数量</param>
|
||||
/// <returns>历史数据列表</returns>
|
||||
public async Task<List<MarketDataResponse>> GetCryptoHistoricalDataAsync(string symbol, string timeframe, int limit)
|
||||
{
|
||||
// 待接入 OKX API
|
||||
throw new NotImplementedException("加密货币数据源待接入 OKX API");
|
||||
try
|
||||
{
|
||||
_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>
|
||||
@ -195,3 +289,25 @@ internal class TiingoCryptoBar
|
||||
public decimal? volume { 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; }
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user