AssetManager.API/AssetManager.Infrastructure/Services/OkxMarketService.cs
OpenClaw Agent da12f6d163 fix: 修复编译错误和警告
- PortfolioFacade: 使用同步方法 GetStrategyById
- OkxMarketService: 响应模型属性改为可空类型
2026-03-15 13:18:24 +00:00

145 lines
4.6 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Net.Http.Json;
using AssetManager.Models.DTOs;
using Microsoft.Extensions.Logging;
namespace AssetManager.Infrastructure.Services;
/// <summary>
/// OKX 加密货币市场数据服务接口
/// </summary>
public interface IOkxMarketService
{
/// <summary>
/// 获取加密货币实时价格
/// </summary>
Task<MarketPriceResponse> GetCryptoPriceAsync(string symbol);
/// <summary>
/// 获取加密货币历史K线数据
/// </summary>
Task<List<MarketDataResponse>> GetCryptoHistoricalDataAsync(string symbol, string timeframe, int limit);
}
/// <summary>
/// OKX 加密货币市场数据服务实现
/// </summary>
public class OkxMarketService : IOkxMarketService
{
private readonly ILogger<OkxMarketService> _logger;
private readonly HttpClient _httpClient;
public OkxMarketService(ILogger<OkxMarketService> logger, IHttpClientFactory httpClientFactory)
{
_logger = logger;
_httpClient = httpClientFactory.CreateClient();
}
public async Task<MarketPriceResponse> GetCryptoPriceAsync(string symbol)
{
_logger.LogInformation("OKX 获取加密货币价格: {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,
Timestamp = DateTime.UtcNow,
AssetType = "Crypto"
};
}
public async Task<List<MarketDataResponse>> GetCryptoHistoricalDataAsync(string symbol, string timeframe, int limit)
{
_logger.LogInformation("OKX 获取历史数据: {Symbol}, 周期: {Timeframe}, 数量: {Limit}", symbol, timeframe, 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"
});
}
}
// 按时间升序排列
return result.OrderBy(x => x.Timestamp).ToList();
}
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"
};
}
}
// 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; }
}
internal class OkxCandlesResponse
{
public string? code { get; set; }
public string? msg { get; set; }
public List<string[]>? data { get; set; }
}